我面临着下一个误会。

序言:

我有带有下一个基本UI部分的wpf应用程序:RadioButtons和一些基于Popup使用下拉列表的控件(以组合框的方式)。根据某种逻辑,每个单选按钮都将挂接PreviewMouseDown事件并进行一些计算。
在下一种情况下,

  • 用户打开弹出窗口(不选择任何内容,弹出窗口保持打开状态)
  • 用户单击单选按钮

  • 不会按预期为单选按钮触发PreviewMouseDown(因为Popup feature)。

    我的目标是尽管有一个,但也将PreviewMouseDown触发为RadioButton

    尝试解决:

    快速而肮脏的解决方案是:钩住PreviewMouseDown以获得Popup,并在需要时使用新的源重新激发PreviewMouseDown事件,并使用单选按钮作为源。可以通过MouseButtonEventArgs.MouseDevice.DirectlyOver获得新的源。下一段代码可以做到这一点(仅当Popup“吃掉” PreviewMouseDown进行外部点击时,事件才会重新触发):
        private static void GrantedPopupPreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            var popup = sender as Popup;
            if(popup == null)
                return;
    
            var realSource = e.MouseDevice.DirectlyOver as FrameworkElement;
            if(realSource == null || !realSource.IsLoaded)
                return;
    
            var parent = LayoutTreeHelper.GetParent<Popup>(realSource);
    
            if(parent == null || !Equals(parent, popup ))
            {
                e.Handled = true;
                var args = new MouseButtonEventArgs(e.MouseDevice,
                                                    e.Timestamp,
                                                    e.ChangedButton)
                {
                    RoutedEvent = UIElement.PreviewMouseDownEvent,
                    Source = e.MouseDevice.DirectlyOver,
                };
                realSource.RaiseEvent(args);
            }
        }
    

    如果我通过Popup.PreviewMouseDown附加一个处理程序,则直接通过Behavior将该处理程序直接附加到PreviewMouseDown时,此效果很好,但如果我通过EventManager.RegisterClassHandler附加一个处理程序,则不起作用。这些单选按钮可能出现在页面上):
    EventManager.RegisterClassHandler(
                typeof (Popup),
                PreviewMouseDownEvent,
                new MouseButtonEventHandler(GrantedPopupPreviewMouseDown));
    

    调试器显示Popup(请参见上面的代码)是e.MouseDevice.DirectlyOver,而不是Popup(就像我通过Radiobutton附加处理程序时一样)!

    问题:

    如果事件处理程序以两种不同的方式附加,那么对于同一操作,Behavior如何且为何不同?

    有人可以解释这种行为吗?

    非常感谢。

    最佳答案

    提供组合框是用户从一组选项中进行选择的一种方式,您可能想要这样做。但它也有其他契约(Contract)。它说用户应该只专注于此任务,而仅专注于此任务。但这不是您的情况。您要显示这些选项,使其具有隐藏功能,并允许用户在显示这些选项时执行其他操作。

    我认为您需要组合框而不是组合框。我的建议是使用包含列表框的扩展器。鉴于:

    class NotificationObject : INotifyPropertyChanged
    {
        public void RaisePropertyChanged(string name)
        {
            if(PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
    
    class ComboEntry : NotificationObject
    {
        public string Name { get; private set; }
    
        private string _option = "Off";
        public string Option
        {
            get { return _option; }
            set { _option = value; RaisePropertyChanged("Option"); }
        }
    
        public ComboEntry()
        {
            Name = Guid.NewGuid().ToString();
        }
    }
    
    class MyDataContext : NotificationObject
    {
        public ObservableCollection<ComboEntry> Entries { get; private set; }
    
        private ComboEntry _selectedEntry;
        public ComboEntry SelectedEntry
        {
            get { return _selectedEntry; }
            set { _selectedEntry = value; RaisePropertyChanged("SelectedEntry"); }
        }
        public MyDataContext()
        {
            Entries = new ObservableCollection<ComboEntry>
            {
                new ComboEntry(),
                new ComboEntry(),
                new ComboEntry()
            };
            SelectedEntry = Entries.FirstOrDefault();
        }
        public void SetOption(string value)
        {
            Entries
                .ToList()
                .ForEach(entry => entry.Option = value);
        }
    }
    

    我认为您需要以下XAML:
    <Window x:Class="RadioInCombo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:RadioInCombo"
        Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <local:MyDataContext x:Key="myDataContext" />
            <DataTemplate x:Key="ComboEntryTemplate">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Name}" />
                    <Border Width="5" />
                    <TextBlock Text="{Binding Option}" />
                </StackPanel>
            </DataTemplate>
        </Window.Resources>
        <StackPanel DataContext="{StaticResource myDataContext}">
            <RadioButton x:Name="OnButton"
                         Content="On"
                         PreviewMouseDown="OnButton_PreviewMouseDown" />
            <RadioButton x:Name="OffButton"
                         Content="Off"
                         PreviewMouseDown="OffButton_PreviewMouseDown" />
            <Expander Header="{Binding SelectedEntry}"
                      HeaderTemplate="{StaticResource ComboEntryTemplate}">
                <ListBox ItemsSource="{Binding Entries}"
                         ItemTemplate="{StaticResource ComboEntryTemplate}" />
            </Expander>
        </StackPanel>
    </Window>
    

    以及以下代码:
        private MyDataContext GetMyDataContext()
        {
            var candidate = FindResource("myDataContext") as MyDataContext;
            if (candidate == null) throw new ApplicationException("Could not locate the myDataContext object");
            return candidate;
        }
    
        private void OnButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            GetMyDataContext().SetOption("On");
        }
    
        private void OffButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            GetMyDataContext().SetOption("Off");
        }
    

    关于c# - MouseButtonEventArgs.MouseDevice.DirectlyOver的误解,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15876285/

    10-11 11:56