6

我面临着下一个误解。

序言

我有 wpf 应用程序,其中包含下一个基本 UI 部分:RadioButtons以及一些基于Popup(以组合框方式)使用下拉菜单的控件。根据一些逻辑每个单选按钮挂钩PreviewMouseDown事件并做一些计算。在下一个场景中,

  1. 用户打开弹出窗口(不要选择任何东西,弹出窗口只是保持打开状态)
  2. 用户单击单选按钮

PreviewMouseDown不会像预期的那样为单选按钮触发(因为Popup feature)。

尽管有一个,我的目标仍然是射击PreviewMouseDownRadioButton

尝试解决

快速而肮脏的解决方案是:如果需要,使用新源挂钩并重新触发事件,使用单选按钮作为PreviewMouseDownPopupPreviewMouseDown新的来源可以通过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.PreviewMouseDownBehaviorPreviewMouseDownEventManager.RegisterClassHandlerPopup

EventManager.RegisterClassHandler(
            typeof (Popup),
            PreviewMouseDownEvent,
            new MouseButtonEventHandler(GrantedPopupPreviewMouseDown));

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

问题

如果事件处理程序以两种不同的方式附加,相同的操作如何以及为什么MouseButtonEventArgs会有所不同?

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

非常感谢。

4

1 回答 1

0

提供组合框是用户从一组选项中进行选择的一种方式,您可能希望这样做。但它也有其他合同。它说用户应该专注于这个并且只关注这个任务。但这不是你的情况。您想要显示选项,让它们可以隐藏,并允许用户在显示时执行其他操作。

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

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");
    }
于 2013-10-18T15:22:18.473 回答