Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/299.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 焦点抽象_C#_Wpf_Mvvm_Focus - Fatal编程技术网

C# 焦点抽象

C# 焦点抽象,c#,wpf,mvvm,focus,C#,Wpf,Mvvm,Focus,带有按钮的UserControl(其中一些按钮已禁用)嵌套在其他UserControl中。窗口中同时显示多个这样的窗口 现在我需要将焦点设置为嵌套的UserControl的第一个启用按钮,而选择焦点的逻辑将在窗口级别上运行(例如,当窗口将启用某些UserControl) 我需要能够(通过属性?)通过几个ViewModels传递焦点请求,并最终在嵌套的UserControl视图中触发它 我可以抽象焦点请求吗?例如,我希望能够告诉“将焦点设置为此高级用户控件”,并且应该以某种方式自动通过嵌套的Use

带有按钮的
UserControl
(其中一些按钮已禁用)嵌套在其他
UserControl
中。窗口中同时显示多个这样的窗口

现在我需要将焦点设置为嵌套的
UserControl
的第一个启用按钮,而选择焦点的逻辑将在窗口级别上运行(例如,当窗口将启用某些
UserControl

我需要能够(通过属性?)通过几个ViewModels传递焦点请求,并最终在嵌套的
UserControl
视图中触发它

我可以抽象焦点请求吗?例如,我希望能够告诉“将焦点设置为此高级用户控件”,并且应该以某种方式自动通过嵌套的
UserControl
及其按钮,因为只有按钮可以接收焦点的元素

伪代码:

// in window
UserControlA.Focus();

// should in fact set focus to 4th button of nested user control
UserControlA.UserControlB.ButtonD.Focus();

// because of data templates it is actually more like this
var nested = UserControlA.ContentControl.Content as UserControlB;
var firstEnabledButton = nested.ItemsControl[3] as Button;
firstEnabledButton.SetFocus();

// and because of MVVM it may be as simple as
ViewModelA.IsFocused = true;
// but then A should run
ViewModelB.IsFocused = true;
// and then B should set property of button ViewModel
Buttons.First(o => o.IsEnabled).IsFocused = true.
// and then this has to be somehow used by the view (UserControlB) to set focus...
问题不在于如何在MVVM中设置焦点,这在某种程度上(对于触发器,它需要在属性第一次设置为
false
)时进行难看的变通。我的问题是如何传递该请求(“然后…,然后…,然后…”,在上面的示例中)

有什么想法吗


我正在寻找一个简单直观、可重用性最高的xaml解决方案。我不想用
…IsFocused
属性和绑定垃圾邮件发送每个ViewModel和视图

我可以用一些副作用来发挥我的优势,例如,考虑这种行为

public static bool GetFocusWhenEnabled(DependencyObject obj) => (bool)obj.GetValue(FocusWhenEnabledProperty);
public static void SetFocusWhenEnabled(DependencyObject obj, bool value) => obj.SetValue(FocusWhenEnabledProperty, value);

public static readonly DependencyProperty FocusWhenEnabledProperty =
    DependencyProperty.RegisterAttached("FocusWhenEnabled", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, (d, e) =>
    {
        var element = d as UIElement;
        if (element == null)
            throw new ArgumentException("Only used with UIElement");
        if ((bool)e.NewValue)
            element.IsEnabledChanged += FocusWhenEnabled_IsEnabledChanged;
        else
            element.IsEnabledChanged -= FocusWhenEnabled_IsEnabledChanged;
    }));

static void FocusWhenEnabled_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    var element = (UIElement)sender;
    if (element.IsEnabled)
        element.Dispatcher.InvokeAsync(() => element.Focus()); // invoke is a must
}
可用于自动聚焦启用的元素。除此之外,这需要启用一些
逻辑,并且在一些复杂的场景中很容易停止工作(启用不应导致聚焦)



我在想,在尝试将焦点设置为容器时,是否可以通过xaml(仅使用xaml)向传递焦点请求添加一些附加属性,

我想你应该考虑使用这个方法和 CopyWaveStudio方向。下一个——这通常应该给你预期的结果,即把焦点放在第一个可以接收键盘焦点的控件上。特别是,这意味着将省略非焦点控件、禁用控件和无法接收键盘焦点的控件(例如
ItemsControl
UserControl
等)。这里唯一需要注意的是控件将以选项卡顺序进行遍历,但除非您在这方面做了手脚,否则它应该以深度优先的预顺序方式遍历可视化树。所以这个代码:

UserControlA.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
如果
UserControlA.UserControlB.button是
UserControlA
的第一个可聚焦并启用的子键盘,则应将焦点放在
UserControlA.UserControlB.button上

以下是我将要做的事情的背后不需要使用代码的原因。首先,我将放弃使用视图模型属性来控制焦点。在我看来,移动焦点更像是基于请求的概念,而不是基于状态的,因此我会使用事件(例如,
focusrequest
)。为了使其可重用,我需要创建一个单事件接口(例如,
IRequestFocus
)。最后一步是创建一个行为,该行为将自动检查附加对象的
DataContext
是否实现了
IRequestFocus
,并在每次引发
FocusRequested
事件时调用
MoveFocus


使用这种设置,您只需在
ViewModelA
中实现
IRequestFocus
,并将行为附加到
UserControlA
。然后简单地提高<代码>焦点> <代码> > < ViewModelA >代码>将焦点移到<代码>用户控件.USECONBR.ButOND

< P>我认为您应该考虑使用该方法和 CopyWaveStudio方向。即,将焦点赋予第一个遇到的可接收键盘焦点的控件。特别是,这意味着将省略非焦点控件、禁用控件和无法接收键盘焦点的控件(例如
ItemsControl
UserControl
等)。这里唯一需要注意的是控件将以选项卡顺序进行遍历,但除非您在这方面做了手脚,否则它应该以深度优先的预顺序方式遍历可视化树。所以这个代码:

UserControlA.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
如果
UserControlA.UserControlB.button是
UserControlA
的第一个可聚焦并启用的子键盘,则应将焦点放在
UserControlA.UserControlB.button上

以下是我将要做的事情的背后不需要使用代码的原因。首先,我将放弃使用视图模型属性来控制焦点。在我看来,移动焦点更像是基于请求的概念,而不是基于状态的,因此我会使用事件(例如,
focusrequest
)。为了使其可重用,我需要创建一个单事件接口(例如,
IRequestFocus
)。最后一步是创建一个行为,该行为将自动检查附加对象的
DataContext
是否实现了
IRequestFocus
,并在每次引发
FocusRequested
事件时调用
MoveFocus


使用这种设置,您只需在
ViewModelA
中实现
IRequestFocus
,并将行为附加到
UserControlA
。然后,只需在
ViewModelA
中提高
focusrequest
即可将焦点移动到
UserControlA.UserControlB.button

使用
MoveFocus
是一个好主意,谢谢。它可以在任何容器上使用,将焦点传递到(或进一步传递),仅此一点就完美地解决了我当前的问题,因为我不再需要在ViewModel中处理
IsFocused
,只需在视图中调用它,它就会自动在某个地方设置焦点。尽管像往常一样,这需要使用DispatcherPriority.Render调用