使用户控件在WPF中可访问

使用户控件在WPF中可访问,wpf,user-controls,accessibility,Wpf,User Controls,Accessibility,我有一个WPF表单,它由两列组成的网格组成 左侧列中是控件标签,右侧列中是我的控件 这些控件都是用户控件。在最简单的情况下,其中一些控件只是包装现有的WPF控件,例如textbox,以便它们都实现一个公共接口 生成表单时,我有这样的代码来设置关联控件的标签,其中newControl是创建的UserControl和ctl。Caption只返回所需的标签文本: Label newLabel = new Label(); newLabel.Content = ctl.Caption + ":"; ne

我有一个WPF表单,它由两列组成的网格组成

左侧列中是控件标签,右侧列中是我的控件

这些控件都是用户控件。在最简单的情况下,其中一些控件只是包装现有的WPF控件,例如textbox,以便它们都实现一个公共接口

生成表单时,我有这样的代码来设置关联控件的标签,其中newControl是创建的UserControl和ctl。Caption只返回所需的标签文本:

Label newLabel = new Label();
newLabel.Content = ctl.Caption + ":";
newLabel.Target = newControl;
一个问题是设定目标实际上不起作用。如果标题中有下划线,则助记符键不会将焦点设置为包装控件。一种解决方法是在UserControl代码中手动将焦点设置为包装控件-但是

最大的问题是可访问性。屏幕阅读器(如JAWS和Windows内置的“讲述人”)在控件接收焦点时不会读取控件标题

我已经看过这个:-它提供了很多细节,但没有有用的例子。它有很多关于自定义控件的东西,对于一个简单的用户控件来说,这肯定是过分了吧

那么,如何将标签正确地“附加”到我的用户控件上


您可以在以下位置浏览整个项目的代码-特定代码在EditorControl项目中,UserControls在ElementEditor.xaml.cs中实例化。

hmmm我曾试图在一个小测试项目上重现您的问题,但对我来说,它起作用了。。。因此,我想您必须提供有关如何构建用户控件的更多详细信息。以下是对我有效的方法:

我创建了一个空项目(和往常一样,只有应用程序和窗口文件),并在我的窗口中设置了一个包含两列的网格:

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Name="Window"
        SizeToContent="WidthAndHeight">

    <Grid Name="MyGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
    </Grid>

</Window>
它基本上是一个带有“标题”dp的文本框

现在在我的窗口的代码隐藏中:

public MainWindow()
{
    InitializeComponent();

    MyTextBox tb = new MyTextBox { Caption = "_Foo", Width = 100 };
    Label lb = new Label { Content = tb.Caption + ":", Target = tb };

    MyGrid.Children.Add(lb);
    MyGrid.Children.Add(tb);

    Grid.SetColumn(lb, 0);
    Grid.SetColumn(tb, 1);
}
有了这个,当我按下ALT+F时,我的注意力确实集中在TB上(我甚至可以在按下ALT时看到标签中“Foo”的F下的uu)

因此,我想您的问题与您的用户控件本身以及它们是如何构建的有关(例如,什么样的模板)

编辑:

如果您的控件不是扩展现有控件,而是包含WPF控件,那么问题可能在于焦点方法。您应该添加一个Focus()方法,当控件本身获得焦点时,该方法将焦点设置在控件的右侧

代码(例如,对于包含要获取焦点的文本框的UserControl):

编辑2: 我曾经遇到一个问题,将焦点转移到dataGridCell中的一个子角色,下面是我在模板中所做的:

 <ControlTemplate.Triggers>
     <Trigger Property="IsFocused" Value="True">
          <Setter TargetName="TextBoxPart" Property="FocusManager.FocusedElement" Value="{Binding ElementName=TextBoxPart}" />
     </Trigger>
 </ControlTemplate.Triggers>

您可以尝试将其添加到模板中。这会转移你的注意力


至于可访问性,我认为这不会有什么帮助,但我看不到任何实现您想要的方法:-/

您的新控件是一种不允许添加额外内容的控件类型。 如果您想向其中添加一些内容,您需要使用支持它的类,如ContentControl或Panel(对于多个孩子),您可以实现自己的控件来实现IAddChild接口

对于您的问题,一个简单的解决方案可以是:

<UserControl x:Class="UIMocks.MyCustomControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <StackPanel x:Name="content"/>
</UserControl>
然后你可以使用

MyCustomControl newControl = InitialiseEditorControl(ctl);
...
Label newLabel = new Label();
newLabel.Content = ctl.Caption + ":";
newControl.Children.Add(newLabel);

我将使用decorator模式,因为它允许您在XAML中“包装”控件(即,不必创建自定义控件)。今天晚些时候下班后我将发布一个示例。在我的例子中,我没有扩展任何现有控件,只是包装它们。例如,我可能有一些MyCustomTextBoxControl,它可能包含一个文本框,但不是从中派生出来的(可能是因为它旁边还有一个按钮或其他东西)。谢谢David,我刚刚尝试了一下,但似乎没有任何效果。即使这样,可访问性仍然被打破——当使用“讲述人”或“大白鲨”时,将注意力集中在文本框上仍然无法读出标签标题。。。明天我会进一步考虑你的问题的。如果有帮助(可能有些过分),您可以从下载整个源代码-问题代码在EditorControl项目中,UserControls在ElementEditor.xaml.cs中实例化。我终于找到了我要找的代码,请参阅我的第二次编辑。希望这能解决至少一半的问题。至于其余的…AddChild在ContentControl上受保护,因此无法在我的AddControlToGrid函数中访问它。并且用户控件不能强制转换到面板。此外,从文档中可以看出,.net 4中不推荐使用IAddChild。没错,IAddChild现在已过时,它已被e ContentProperty属性替换。谢谢你说出来。我并不是说他应该将他的usercontrol转换成一个面板,而是说他可以使用一个包含类似面板内容的控件(它是抽象的,不能直接使用)
public partial class MyTextBox : TextBox
{
    public static readonly DependencyProperty CaptionProperty =
         DependencyProperty.Register("Caption", typeof(string), typeof(MyTextBox), new UIPropertyMetadata(""));
    public string Caption
    {
        get { return (string)GetValue(CaptionProperty); }
        set { SetValue(CaptionProperty, value); }
    }

    public MyTextBox()
    {
        InitializeComponent();
    }

    protected override void OnGotFocus(RoutedEventArgs e)
    {
        TextBoxPart.Focus();
    }
}
 <ControlTemplate.Triggers>
     <Trigger Property="IsFocused" Value="True">
          <Setter TargetName="TextBoxPart" Property="FocusManager.FocusedElement" Value="{Binding ElementName=TextBoxPart}" />
     </Trigger>
 </ControlTemplate.Triggers>
<UserControl x:Class="UIMocks.MyCustomControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <StackPanel x:Name="content"/>
</UserControl>
[ContentProperty("Children")]
public partial class MyCustomControl : UserControl
{
    public MyCustomControl()
    {
        InitializeComponent();
    }

    public UIElementCollection Children { get { return content.Children; } }
}
MyCustomControl newControl = InitialiseEditorControl(ctl);
...
Label newLabel = new Label();
newLabel.Content = ctl.Caption + ":";
newControl.Children.Add(newLabel);