c# - 绑定(bind)到自定义控件的一部分中的事件

标签 c# wpf xaml wpf-controls

我正在尝试实现以下位置的示例:

https://github.com/olohmann/WpfRxControls

自定义控件分为三个部分:

PART_TextBox

PART_Popup

PART_ListBox

相关来源:

https://github.com/olohmann/WpfRxControls/blob/master/WpfRxControls/AutoCompleteTextBox.cs

https://github.com/olohmann/WpfRxControls/blob/master/WpfRxControls/Themes/Generic.xaml

所有部件都已就位,使用新控件的代码如下:

<ctrls:AutoCompleteTextBox
            Grid.Row="1"
            AutoCompleteQueryResultProvider="{Binding AutoCompleteQueryResultProvider}"
            Margin="10" FontSize="20" PopupHeight="300">

</ctrls:AutoCompleteTextBox>

我只需要在我的页面 XAML/ViewModel 中 Hook ListBox 的 SelectionChanged 事件,如何实现这一点?

编辑:在XAML/VM中,不查看隐藏代码。到目前为止,所有 View 代码后面都是空的,我想保持这种状态。

我认为有某种方法可以在 MainWindow.XAML 中的 ControlTemplate 覆盖中覆盖 PART_ListBox?

编辑:最终解决方案,感谢mm8

在 AutoCompleteTextBox.cs 中,创建 ICommand 类型的依赖属性:

public const string AutoCompleteSelectionChangedPropertyName = "AutoCompleteSelectionChangedCommand";

        public ICommand AutoCompleteSelectionChangedCommand
        {
            get { return (ICommand) GetValue(AutoCompleteSelectionChangedProperty); }
            set { SetValue(AutoCompleteSelectionChangedProperty, value);}
        }

        public static readonly DependencyProperty AutoCompleteSelectionChangedProperty = DependencyProperty.Register(
            AutoCompleteSelectionChangedPropertyName,
            typeof(ICommand),
            typeof(AutoCompleteTextBox));

在SetResultText方法中:

AutoCompleteSelectionChangedCommand?.Execute(autoCompleteQueryResult);

View /ViewModel 用法:

<ac:AutoCompleteTextBox Name="AutoComplete" 
    AutoCompleteQueryResultProvider="{Binding AutoCompleteQueryResultProvider}" 
    FontSize="12"
    AutoCompleteSelectionChangedCommand="{Binding CommandEditValueChanged}">
</ac:AutoCompleteTextBox>


public ICommand CommandEditValueChanged { get; set; }

public MainWindowViewModel(){
     CommandEditValueChanged = new DelegateCommand<object>(OnEditValueChanged);
}

private void OnEditValueChanged(object result){
     // do stuff
}

最佳答案

您可以在 View 中处理 AutoCompleteTextBox 的 Loaded 事件,使用 FindName 方法获取对控件模板中的 PART_ListBox 的引用,然后为 ListBox 的 SelectionChanged 事件挂接一个事件处理程序:

<ctrls:AutoCompleteTextBox
            Grid.Row="1"
            AutoCompleteQueryResultProvider="{Binding AutoCompleteQueryResultProvider}"
            Margin="10" FontSize="20" PopupHeight="300" Loaded="AutoCompleteTextBox_Loaded">

</ctrls:AutoCompleteTextBox>

private void AutoCompleteTextBox_Loaded(object sender, RoutedEventArgs e)
{
    AutoCompleteTextBox actb = sender as AutoCompleteTextBox;
    ListBox lb = actb.Template.FindName("PART_ListBox", actb) as ListBox;
    if (lb != null)
    {
        lb.SelectionChanged += (ss, ee) =>
        {
            MainWindowViewModel vm = DataContext as MainWindowViewModel;
            //invoke a command of the view model or do whatever you want here...
            var selectedItem = lb.SelectedItem;
        };
    }
}

您的 View 模型类没有(也不应该有任何)关于作为控件模板一部分的 ListBox 的引用或知识。

I thought there was some way to override PART_ListBox in a ControlTemplate override in MainWindow.XAML?

然后您将必须覆盖/重新定义 AutoCompleteTextBox 控件的整个 ControlTemplate,这似乎有点不必要。

MVVM 不是从 View 中消除代码 - 它是关于关注点分离以及是否从 View 的 XAML 标记或同一 View 的代码隐藏中连接事件处理程序就设计模式而言,没有任何区别。

编辑:但是,如果您想保持代码隐藏类的干净,您可以使用附加行为来实现:

public class AutoCompleteBoxBehavior
{
    public static ICommand GetSelectionChangedCommand(AutoCompleteTextBox actb)
    {
        return (ICommand)actb.GetValue(SelectionChangedCommandProperty);
    }

    public static void SetSelectionChangedCommand(AutoCompleteTextBox actb, ICommand value)
    {
        actb.SetValue(SelectionChangedCommandProperty, value);
    }

    public static readonly DependencyProperty SelectionChangedCommandProperty =
        DependencyProperty.RegisterAttached(
        "SelectionChangedCommand",
        typeof(ICommand),
        typeof(AutoCompleteBoxBehavior),
        new UIPropertyMetadata(null, OnHandleSelectionChangedEvent));

    private static void OnHandleSelectionChangedEvent(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ICommand command = e.NewValue as ICommand;
        if(command != null)
        {
            AutoCompleteTextBox actb = d as AutoCompleteTextBox;
            actb.Loaded += (ss, ee) =>
            {
                ListBox lb = actb.Template.FindName("PART_ListBox", actb) as ListBox;
                if (lb != null)
                {
                    lb.SelectionChanged += (sss, eee) =>
                    {
                        command.Execute(null);
                    };
                }
            };
        }
    }
}

<ctrls:AutoCompleteTextBox
        Grid.Row="1"
        AutoCompleteQueryResultProvider="{Binding AutoCompleteQueryResultProvider}"
        Margin="10" FontSize="20" PopupHeight="300"
        local:AutoCompleteBoxBehavior.SelectionChangedCommand="{Binding YourCommand}">
 </ctrls:AutoCompleteTextBox>

WPF 中的附加行为简介: https://www.codeproject.com/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF

关于c# - 绑定(bind)到自定义控件的一部分中的事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41789030/

相关文章:

c# - 更改绑定(bind)中的默认千位和小数点分隔符

wpf - 从默认样式继承样式

c# - Linq 动态 orderby 不工作

c# - 为 ListBoxItem 绑定(bind) mouseLeftButtonDown 并在 PropertyChanged 上绑定(bind)背景颜色(mvvm、wpf)

c# - 解析 1 TB 的文本并有效地计算每个单词的出现次数

c# - listBox.ItemContainerGenerator.ContainerFromItem为null

wpf - 您如何学习 WPF 和 Silverlight?

c# - 类型引用找不到名为 '{clr-namespace :xxx}ClassName on MergedDictionary

c# - asp.net:如何将类似 LinkBut​​ton 的链接功能与表格单元格背景上的委托(delegate)连接起来?

c# - 如何使用 CodeDOM/T4/PostSharp/whatever 使用 c# 自动创建(例如)单例?