none
自作コンポーネントをナレーターに対応させるには RRS feed

  • 質問

  • 自作のエディターコンポーネントのストアアプリ版をナレーターに対応させようとしているのですが、うまくいきません。文字を入力しても入力された文字をしゃべりませんし、キャレットを移動させても表示されている内容がありませんとかしゃべってくれず、VisualStudioのようにうまく動作しません。キャレット移動時と入力時に入力された内容をしゃべらせるのはどうすればいいんでしょうか。正しい実装法とかがあったら教えてほしいです

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading.Tasks;
    #if METRO
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Automation;
    using Windows.UI.Xaml.Automation.Peers;
    using Windows.UI.Xaml.Automation.Provider;
    using Windows.UI.Xaml.Automation.Text;
    using FooEditEngine.Metro;
    #endif
    #if WPF
    using System.Windows.Automation;
    using System.Windows.Automation.Peers;
    using System.Windows.Automation.Provider;
    using System.Windows.Automation.Text;
    using FooEditEngine.WPF;
    #endif
    
    namespace FooEditEngine
    {
        /// <summary>
        /// Automation Peer class for CustomInputBox2.  
        /// 
        /// Note: The difference between this and CustomControl1AutomationPeer is that this one implements
        /// Text Pattern (ITextProvider) and Value Pattern (IValuePattern) interfaces.  So Touch keyboard shows 
        /// automatically when user taps on the control with Touch or Pen.
        /// </summary>
        sealed class FooTextBoxAutomationPeer : FrameworkElementAutomationPeer, ITextProvider, IValueProvider
        {
            private FooTextBox fooTextBox;
            private string accClass = "FooTextBox";
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="owner"></param>
            public FooTextBoxAutomationPeer(FooTextBox owner)
                : base(owner)
            {
                this.fooTextBox = owner;
                this.fooTextBox.Document.Update += Document_Update;
                this.fooTextBox.Controller.CaretMoved += Controller_CaretMoved;
            }
    
            void Controller_CaretMoved(object sender, EventArgs e)
            {
                this.RaiseAutomationEvent(AutomationEvents.TextPatternOnTextSelectionChanged);
            }
    
            void Document_Update(object sender, DocumentUpdateEventArgs e)
            {
                this.RaiseAutomationEvent(AutomationEvents.TextPatternOnTextChanged);
            }
    
    #if METRO
            /// <summary>
            /// Override GetPatternCore to return the object that supports the specified pattern.  In this case the Value pattern, Text
            /// patter and any base class patterns.
            /// </summary>
            /// <param name="patternInterface"></param>
            /// <returns>the object that supports the specified pattern</returns>
            protected override object GetPatternCore(PatternInterface patternInterface)
            {
                if (patternInterface == PatternInterface.Value)
                {
                    return this;
                }
                else if (patternInterface == PatternInterface.Text)
                {
                    return this;
                }
                return base.GetPatternCore(patternInterface);
            }
    #endif
    #if WPF
            public override object GetPattern(PatternInterface patternInterface)
            {
                if (patternInterface == PatternInterface.Value)
                {
                    return this;
                }
                else if (patternInterface == PatternInterface.Text)
                {
                    return this;
                }
                return base.GetPattern(patternInterface);
            }
    #endif
    
            /// <summary>
            /// Override GetClassNameCore and set the name of the class that defines the type associated with this control.
            /// </summary>
            /// <returns>The name of the control class</returns>
            protected override string GetClassNameCore()
            {
                return this.accClass;
            }
    
            protected override AutomationControlType GetAutomationControlTypeCore()
            {
                return AutomationControlType.Edit;
            }
    
            protected override bool IsContentElementCore()
            {
                return true;
            }
    
    #if METRO
            protected override Windows.Foundation.Rect GetBoundingRectangleCore()
            {
                return new Windows.Foundation.Rect(0, 0, this.fooTextBox.ActualWidth, this.fooTextBox.ActualHeight);
            }
    #endif
    #if WPF
            protected override System.Windows.Rect GetBoundingRectangleCore()
            {
                return new System.Windows.Rect(0, 0, this.fooTextBox.ActualWidth, this.fooTextBox.ActualHeight);
            }
    #endif
    
            #region Implementation for ITextPattern interface
            // Complete implementation of the ITextPattern is beyond the scope of this sample.  The implementation provided
            // is specific to this sample's custom control, so it is unlikely that they are directly transferable to other 
            // custom control.
    
            ITextRangeProvider ITextProvider.DocumentRange
            {
                // A real implementation of this method is beyond the scope of this sample.
                // If your custom control has complex text involving both readonly and non-readonly ranges, 
                // it will need a smarter implementation than just returning a fixed range
                get
                {
                    return new FooTextBoxRangeProvider(this.fooTextBox, this);
                }
            }
    
            ITextRangeProvider[] ITextProvider.GetSelection()
            {
                ITextRangeProvider[] ret = new ITextRangeProvider[1];
                int selStart = this.fooTextBox.SelectionStart;
                int selLength = this.fooTextBox.SelectionLength;
                ret[0] = new FooTextBoxRangeProvider(this.fooTextBox, selStart, selLength, this);
                return ret;
            }
    
            ITextRangeProvider[] ITextProvider.GetVisibleRanges()
            {
                ITextRangeProvider[] ret = new ITextRangeProvider[1];
                if (this.fooTextBox.LayoutLineCollection.Count == 0)
                {
                    ret[0] = new FooTextBoxRangeProvider(this.fooTextBox, 0, 0, this);
                }
                else
                {
    #if METRO
                    int startIndex = this.fooTextBox.GetIndexFromPostion(new Windows.Foundation.Point(0,0));
                    int endIndex = this.fooTextBox.GetIndexFromPostion(new Windows.Foundation.Point(this.fooTextBox.ActualWidth, this.fooTextBox.ActualHeight));
    #endif
    #if WPF
                    int startIndex = this.fooTextBox.GetIndexFromPostion(new System.Windows.Point(0, 0));
                    int endIndex = this.fooTextBox.GetIndexFromPostion(new System.Windows.Point(this.fooTextBox.ActualWidth, this.fooTextBox.ActualHeight));
    #endif
                    ret[0] = new FooTextBoxRangeProvider(this.fooTextBox, startIndex, endIndex - startIndex, this);
                }
                return ret;
            }
    
            ITextRangeProvider ITextProvider.RangeFromChild(IRawElementProviderSimple childElement)
            {
                return new FooTextBoxRangeProvider(this.fooTextBox,0,0, this);
            }
    
    #if METRO
            ITextRangeProvider ITextProvider.RangeFromPoint(Windows.Foundation.Point screenLocation)
            {
                Windows.Foundation.Point pt = Util.GetClientPoint(screenLocation, this.fooTextBox);
    
                int index = this.fooTextBox.GetIndexFromPostion(pt);
                int length = 1;
                if (index == this.fooTextBox.Document.Length)
                    length = 0;
                
                return new FooTextBoxRangeProvider(this.fooTextBox, index, length, this);
            }
    #endif
    #if WPF
            ITextRangeProvider ITextProvider.RangeFromPoint(System.Windows.Point screenLocation)
            {
                System.Windows.Point pt = this.fooTextBox.PointFromScreen(screenLocation);
    
                int index = this.fooTextBox.GetIndexFromPostion(pt);
                int length = 1;
                if (index == this.fooTextBox.Document.Length)
                    length = 0;
    
                return new FooTextBoxRangeProvider(this.fooTextBox, index, length, this);
            }
    #endif
    
            SupportedTextSelection ITextProvider.SupportedTextSelection
            {
                get { return SupportedTextSelection.Single; }
            }
    
            #endregion
    
            #region Implementation for IValueProvider interface
            // Complete implementation of the IValueProvider is beyond the scope of this sample.  The implementation provided
            // is specific to this sample's custom control, so it is unlikely that they are directly transferable to other 
            // custom control.
    
            /// <summary>
            /// The value needs to be false for the Touch keyboard to be launched automatically because Touch keyboard
            /// does not appear when the input focus is in a readonly UI control.
            /// </summary>
            bool IValueProvider.IsReadOnly
            {
                get { return false; }
            }
    
            void IValueProvider.SetValue(string value)
            {
                this.fooTextBox.Document.Replace(0,this.fooTextBox.Document.Length,value);
            }
    
            string IValueProvider.Value
            {
                get
                {
                    return this.fooTextBox.Document.ToString(0,this.fooTextBox.Document.Length);
                }
            }
    
            #endregion //Implementation for IValueProvider interface
    
            public IRawElementProviderSimple GetRawElementProviderSimple()
            {
                return ProviderFromPeer(this);
            }
        }
    
        /// <summary>
        /// A minimal implementation of ITextRangeProvider, used by CustomControl2AutomationPeer
        /// A real implementation is beyond the scope of this sample
        /// </summary>
        sealed class FooTextBoxRangeProvider : ITextRangeProvider
        {
            private FooTextBox textbox;
            private Document document;
            private FooTextBoxAutomationPeer _peer;
            private int start, end;
    
            public FooTextBoxRangeProvider(FooTextBox textbox, FooTextBoxAutomationPeer peer)
                : this(textbox,0,textbox.Document.Length,peer)
            {
            }
            public FooTextBoxRangeProvider(FooTextBox textbox, int start, int length, FooTextBoxAutomationPeer peer)
            {
                this.textbox = textbox;
                this.document = textbox.Document;
                this.start = start;
                this.end = start + length;
                _peer = peer;
            }
    
            public void AddToSelection()
            {
    
            }
    
            public ITextRangeProvider Clone()
            {
                return new FooTextBoxRangeProvider(this.textbox,this.start,this.end - this.start, _peer);
            }
    
            public bool Compare(ITextRangeProvider o)
            {
                FooTextBoxRangeProvider other = o as FooTextBoxRangeProvider;
                if (other == null)
                    throw new ArgumentNullException("null以外の値を指定してください");
                if (this.start == other.start && this.end == other.end)
                    return true;
                else
                    return false;
            }
    
            public int CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint)
            {
                FooTextBoxRangeProvider other = targetRange as FooTextBoxRangeProvider;
                
                if (other == null)
                    throw new ArgumentException("");
    
                if (endpoint == TextPatternRangeEndpoint.Start)
                {
                    if (targetEndpoint == TextPatternRangeEndpoint.Start)
                        return this.Compare(this.start,other.start);
                    if(targetEndpoint == TextPatternRangeEndpoint.End)
                        return this.Compare(this.start,other.end);
                }
                if (endpoint == TextPatternRangeEndpoint.End)
                {
                    if (targetEndpoint == TextPatternRangeEndpoint.Start)
                        return this.Compare(this.start, other.end);
                    if (targetEndpoint == TextPatternRangeEndpoint.End)
                        return this.Compare(this.end, other.end);
                }
                throw new ArgumentException("endpointに未知の値が指定されました");
            }
    
            int Compare(int self, int other)
            {
                if (self < other)
                    return -1;
                else if (self > other)
                    return 1;
                else
                    return 0;
            }
    
            public void ExpandToEnclosingUnit(TextUnit unit)
            {
                if (unit == TextUnit.Character)
                {
                    this.end = this.start;
                    return;
                }
                if (unit == TextUnit.Format || unit == TextUnit.Word || unit == TextUnit.Line)
                {
                    LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
                    int row = layoutLineCollection.GetLineNumberFromIndex(this.start);
                    LineToIndexTableData lineData = layoutLineCollection.GetData(row);
                    this.start = lineData.Index;
                    this.end = lineData.Index + lineData.Length - 1;
                    return;
                }
                if (unit == TextUnit.Paragraph || unit == TextUnit.Page || unit == TextUnit.Document)
                {
                    this.start = 0;
                    this.end = this.document.Length - 1;
                    return;
                }
                throw new NotImplementedException();
            }
    
            public ITextRangeProvider FindAttribute(int attribute, Object value, bool backward)
            {
                return null;
            }
    
            public ITextRangeProvider FindText(String text, bool backward, bool ignoreCase)
            {
                if (backward)
                    throw new NotImplementedException();
                document.SetFindParam(text, false, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
                IEnumerator<SearchResult> it = document.Find();
                if (it.MoveNext())
                {
                    SearchResult sr = it.Current;
                    return new FooTextBoxRangeProvider(this.textbox, sr.Start, sr.End - sr.Start + 1, _peer);
                }
                return null;
            }
    
            public Object GetAttributeValue(int attribute)
            {
                return null;
            }
    
    #if METRO
            public void GetBoundingRectangles(out double[] rectangles)
    #endif
    #if WPF
            public double[] GetBoundingRectangles()
    #endif
            {
                LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
                TextPoint topLeft = layoutLineCollection.GetTextPointFromIndex(this.start);
                TextPoint bottomRight = this.textbox.LayoutLineCollection.GetTextPointFromIndex(this.end);
    
    
    #if METRO
                Windows.Foundation.Point topLeftPos = this.textbox.GetPostionFromTextPoint(topLeft);
                Windows.Foundation.Point bottomRightPos = this.textbox.GetPostionFromTextPoint(bottomRight);
                topLeftPos = Util.GetScreentPoint(topLeftPos, this.textbox);
                bottomRightPos = Util.GetScreentPoint(bottomRightPos, this.textbox);
    #endif
    #if WPF
                System.Windows.Point topLeftPos = this.textbox.GetPostionFromTextPoint(topLeft);
                System.Windows.Point bottomRightPos = this.textbox.GetPostionFromTextPoint(bottomRight);
                topLeftPos = this.textbox.PointToScreen(topLeftPos);
                bottomRightPos = this.textbox.PointToScreen(bottomRightPos);
    #endif
    
                double width = bottomRightPos.X - topLeftPos.X;
                if (width == 0)
                    width = 1;
                Rectangle rect = new Rectangle(topLeftPos.X, topLeftPos.Y,
                     width,
                     bottomRightPos.Y - topLeftPos.Y + layoutLineCollection.GetData(bottomRight.row).Layout.Height);
    
    #if METRO
                rectangles = new double[4]{
                    rect.X,
                    rect.Y,
                    rect.Width,
                    rect.Height
                };
    #endif
    #if WPF
                return new double[4]{
                    rect.X,
                    rect.Y,
                    rect.Width,
                    rect.Height
                };
    #endif
            }
    
            public IRawElementProviderSimple[] GetChildren()
            {
                return new IRawElementProviderSimple[0];
            }
    
            public IRawElementProviderSimple GetEnclosingElement()
            {
                return _peer.GetRawElementProviderSimple();
            }
    
            public String GetText(int maxLength)
            {
                if (this.document.Length == 0)
                    return "";
                int length = this.end - this.start;
                if (maxLength < 0)
                    return this.document.ToString(this.start, length);
                else
                    return this.document.ToString(this.start, (int)Math.Min(length, maxLength));
            }
    
            public int Move(TextUnit unit, int count)
            {
                if (count == 0)
                    return 0;
                switch (unit)
                {
                    case TextUnit.Character:
                        {
                            int oldStart = this.start;
                            this.start += count;
                            if (this.start > this.document.Length - 1)
                            {
                                this.start = this.document.Length - 1;
                                this.end = this.start;
                            }
                            if (this.start < 0)
                            {
                                this.start = 0;
                                this.end = 0;
                            }
                            return this.start - oldStart;
                        }
                    case TextUnit.Line:
                        {
                            LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
                            Controller controller = this.textbox.Controller;
                            TextPoint oldStart = layoutLineCollection.GetTextPointFromIndex(this.start);
                            TextPoint newStart = controller.GetTextPointAfterMoveLine(count, oldStart);
                            this.start = layoutLineCollection.GetIndexFromTextPoint(newStart);
                            LineToIndexTableData lineData = layoutLineCollection.GetData(newStart.row);
                            this.end = lineData.Index + lineData.Length - 1;
                            return newStart.row - oldStart.row + 1;
                        }
                }
                throw new NotImplementedException();
            }
    
            public void MoveEndpointByRange(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint)
            {
                FooTextBoxRangeProvider other = targetRange as FooTextBoxRangeProvider;
    
                if (other == null)
                    throw new ArgumentException("");
    
                if (endpoint == TextPatternRangeEndpoint.Start)
                {
                    if (targetEndpoint == TextPatternRangeEndpoint.Start)
                        this.start = other.start;
                    if (targetEndpoint == TextPatternRangeEndpoint.End)
                        this.start = other.end;
                    if (this.start > this.end)
                        this.end = this.start;
                    return;
                }
                if (endpoint == TextPatternRangeEndpoint.End)
                {
                    if (targetEndpoint == TextPatternRangeEndpoint.Start)
                        this.end = other.start;
                    if (targetEndpoint == TextPatternRangeEndpoint.End)
                        this.end = other.end;
                    return;
                }
                throw new ArgumentException("endpointに未知の値が指定されました");
            }
    
            public int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count)
            {
                if (count == 0)
                    return 0;
                if (unit == TextUnit.Character)
                {
                    if (endpoint == TextPatternRangeEndpoint.Start)
                    {
                        int newStart = this.start + count -1;
                        if (newStart > this.document.Length - 1)
                            newStart = this.document.Length - 1;
                        if (newStart < 0)
                            newStart = 0;
                        this.start = newStart;
                        if(newStart > this.end)
                            this.end = newStart;
                        return newStart - this.start;
                    }
                    else if (endpoint == TextPatternRangeEndpoint.End)
                    {
                        int newEnd = this.end + count - 1;
                        if (newEnd > this.document.Length - 1)
                            newEnd = this.document.Length - 1;
                        if (newEnd < 0)
                            newEnd = 0;
                        if(this.start > newEnd)
                            this.start = newEnd;
                        this.end= newEnd;
                        return newEnd - this.end + 1;
                    }
                }
                if (unit == TextUnit.Format || unit == TextUnit.Word || unit == TextUnit.Line)
                {
                    if (endpoint == TextPatternRangeEndpoint.Start)
                    {
                        LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
                        Controller controller = this.textbox.Controller;
                        TextPoint docend = layoutLineCollection.GetTextPointFromIndex(this.document.Length - 1);
                        TextPoint oldStart = layoutLineCollection.GetTextPointFromIndex(this.start);
                        TextPoint newStart = controller.GetTextPointAfterMoveLine(count, oldStart);
                        if (newStart > docend)
                            newStart = docend;
                        LineToIndexTableData lineData = layoutLineCollection.GetData(newStart.row);
                        this.start = lineData.Index;
                        this.end = lineData.Index + lineData.Length - 1;
                        return newStart.row - oldStart.row;
                    }
                    else if (endpoint == TextPatternRangeEndpoint.End)
                    {
                        LineToIndexTable layoutLineCollection = this.textbox.LayoutLineCollection;
                        Controller controller = this.textbox.Controller;
                        TextPoint oldEnd = layoutLineCollection.GetTextPointFromIndex(this.end);
                        TextPoint newEnd = controller.GetTextPointAfterMoveLine(count, oldEnd);
                        LineToIndexTableData lineData = layoutLineCollection.GetData(newEnd.row);
                        this.start = lineData.Index;
                        this.end = lineData.Index + lineData.Length - 1;
                        return newEnd.row - oldEnd.row;
                    }
                }
                if (unit == TextUnit.Paragraph || unit == TextUnit.Page || unit == TextUnit.Document)
                {
                    this.start = 0;
                    this.end = this.document.Length - 1;
                }
                throw new NotImplementedException();
            }
    
            public void RemoveFromSelection()
            {
            }
    
            public void ScrollIntoView(bool alignToTop)
            {
    #if METRO
                int row = this.textbox.LayoutLineCollection.GetLineNumberFromIndex(alignToTop ? this.start : this.end);
                this.textbox.ScrollIntoView(0, alignToTop);
    #endif
    #if WPF
    #endif
            }
    
            public void Select()
            {
                this.textbox.Select(this.start, this.end - this.start + 1);
            }
        }
    }
    

    追記

    利用しているコンポーネントはこちらになります。http://sourceforge.jp/projects/fooeditor/scm/git/FooEditEngine/tree/master/

    2013年7月2日 7:46