我面临着下一个误会。
序言:
我有带有下一个基本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/