WPF MVVM 弹出窗口,DataGrid 中带有复选框,没有触发事件
WPF MVVM popup with checkBox inside DataGrid no fire events
这是我的目标(简化):创建一个 userControl 以显示可读的 bitField。
示例值为 0x3:要显示选项 1(位 1):启用,选项 2(位 2):启用,选项 3(位 3):禁用 ...
userControl 行为:如果我单击此控件,它会打开一个带有复选框的弹出窗口(如组合框),允许您启用 optionX(这将更改值和显示的文本)。
这是我用于 UserControl 视图的源代码
<UserControl x:Name="userControl" x:Class="MyProg.Views.BitField"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyProg.Views"
mc:Ignorable="d"
>
<Grid x:Name="LayoutRoot" Height="{Binding ActualHeight, ElementName=userControl, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Width="{Binding ActualWidth, ElementName=userControl, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ToggleButton x:Name="TogglePopupButton" Background="Transparent" BorderBrush="Transparent" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Path=Text}" Grid.Column="0" VerticalAlignment="Center" Margin="0,-5,0,-5" />
<Path x:Name="Arrow" Grid.Column="1" Fill="Black" VerticalAlignment="Center" Data="M0,0 L0,2 L4,6 L8,2 L8,0 L4,4 z" HorizontalAlignment="Right"/>
</Grid>
</ToggleButton>
<Popup x:Name="ToggledPopup" StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=TwoWay}" Width="{Binding ActualWidth, ElementName=userControl, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1">
<Border Background="White" BorderThickness="1" BorderBrush="Black">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox Content="option 1" Grid.Column="0" />
<CheckBox Content="option 2" Grid.Column="2" />
<CheckBox Content="option 3" Grid.Column="4" />
</Grid>
</ScrollViewer>
</Border>
</Popup>
</Grid>
</UserControl>
绑定值背后的代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MyProg.ViewModels;
namespace MyProg.Views
{
/// <summary>
/// Interaction logic for BitField.xaml
/// </summary>
public partial class BitField : UserControl
{
internal vmBitField m_ViewModel;
public BitField()
{
InitializeComponent();
// Load the viewModel
m_ViewModel = new vmBitField();
LayoutRoot.DataContext = m_ViewModel;
m_ViewModel.PropertyChanged += M_ViewModel_PropertyChanged;
}
private void M_ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Value")
{
if(IntValue != m_ViewModel?.Value)
IntValue = (int)m_ViewModel.Value;
}
}
/// <summary>
/// Gets or sets the value which is displayed
/// </summary>
public int IntValue
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value);}
}
/// <summary>
/// Identified the Value dependency property
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("IntValue", typeof(int), typeof(BitField), new PropertyMetadata(0, OnIntValueSet));
private static void OnIntValueSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((BitField)d).m_ViewModel.Value = (uint?)(int)e.NewValue;
}
}
}
和用户控件视图模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MyProg.MVVM;
namespace MyProg.ViewModels
{
class vmBitField : ViewModelBase
{
#region Members
protected uint m_Value = 0; // 7
#endregion
#region Properties
public uint? Value { get { return m_Value; } set { if (value != m_Value) { m_Value = (uint)value; RaiseEventPropertyChanged("Option1"); RaiseEventPropertyChanged("Option2"); RaiseEventPropertyChanged("Option3"); RaiseEventPropertyChanged("Text"); } } }
public bool Option1 { get {return (Value & 0x1) == 1; } set { if (value != Option1) { m_Value ^= 0x1; RaiseEventPropertyChanged("Option1"); RaiseEventPropertyChanged("Value"); RaiseEventPropertyChanged("Text"); } } }
public bool Option2 { get { return (Value & 0x2) == 1; } set { if (value != Option2) { m_Value ^= 0x2; RaiseEventPropertyChanged("Option2"); RaiseEventPropertyChanged("Value"); RaiseEventPropertyChanged("Text"); } } }
public bool Option3 { get { return (Value & 0x4) == 1; } set { if (value != Option3) { m_Value ^= 0x4; RaiseEventPropertyChanged("Option3"); RaiseEventPropertyChanged("Value"); RaiseEventPropertyChanged("Text"); } } }
public string Text { get { return "Option1:" + Option1 + " Option2:" + Option2 + " Option3:" + Option3; } }
#endregion
#region Contructors
public vmBitField()
{
}
#endregion
#region Methodes
#endregion
}
}
当我在页面或 windows.
中插入此 userControl 时,这可以正常工作
<local:LockType IntValue="{Binding Path=ValueAsInt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
现在,我有一个包含很多位域的列表。我想在数据网格中显示所有这些。所以我想将这个 userControl 插入到 dataGrid 中。
这是添加数据网格的代码
<DataGrid Grid.Row="13" Grid.ColumnSpan="3" AutoGenerateColumns="False" Name="dataGrid1" AlternationCount="2"
ItemsSource="{Binding Values}"
CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" CanUserReorderColumns="False" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Id" HeaderStyle="{StaticResource DataGridHeaderCenter}"
CellTemplate="{StaticResource IdText}"/>
<DataGridTemplateColumn Header="Value" Width="400" HeaderStyle="{StaticResource DataGridHeaderCenter}" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local1:LockType IntValue="{Binding Path=ValueAsInt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="CurrentValue"
CellTemplate="{StaticResource CurrentValueText}" />
</DataGrid.Columns>
</DataGrid>
当我的用户控件位于数据网格中时,当我单击切换按钮时,它会正确打开弹出窗口,但随后我无法与弹出窗口中的任何复选框进行交互(选中或取消选中)。
谁能帮我找出为什么弹出窗口中的复选框没有触发事件?
如果需要,我可以添加更多信息。
最好的问候
JM
您必须将弹出窗口的 FocusManager.IsFocusScope 设置为 true。
您还可以通过样式 setter.
设置单元格的 IsFocussable 属性
<Style TargetType="{x:Type DataGridCell}"> <Setter Property="Focusable" Value="False"></Setter> </Style>
弹窗不让你编辑任何东西的问题是因为单元格编辑还没有结束。因此,您也可以通过注册 DataGrid_CellEditEnding 活动来执行以下操作。
private static void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.Column.GetType() == typeof(DataGridTemplateColumn))
{
var popup = GetVisualChild<Popup>(e.EditingElement);
if (popup != null && popup.IsOpen)
{
e.Cancel = true;
}
}
}
这是我的目标(简化):创建一个 userControl 以显示可读的 bitField。
示例值为 0x3:要显示选项 1(位 1):启用,选项 2(位 2):启用,选项 3(位 3):禁用 ...
userControl 行为:如果我单击此控件,它会打开一个带有复选框的弹出窗口(如组合框),允许您启用 optionX(这将更改值和显示的文本)。
这是我用于 UserControl 视图的源代码
<UserControl x:Name="userControl" x:Class="MyProg.Views.BitField"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyProg.Views"
mc:Ignorable="d"
>
<Grid x:Name="LayoutRoot" Height="{Binding ActualHeight, ElementName=userControl, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Width="{Binding ActualWidth, ElementName=userControl, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ToggleButton x:Name="TogglePopupButton" Background="Transparent" BorderBrush="Transparent" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Path=Text}" Grid.Column="0" VerticalAlignment="Center" Margin="0,-5,0,-5" />
<Path x:Name="Arrow" Grid.Column="1" Fill="Black" VerticalAlignment="Center" Data="M0,0 L0,2 L4,6 L8,2 L8,0 L4,4 z" HorizontalAlignment="Right"/>
</Grid>
</ToggleButton>
<Popup x:Name="ToggledPopup" StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=TwoWay}" Width="{Binding ActualWidth, ElementName=userControl, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1">
<Border Background="White" BorderThickness="1" BorderBrush="Black">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox Content="option 1" Grid.Column="0" />
<CheckBox Content="option 2" Grid.Column="2" />
<CheckBox Content="option 3" Grid.Column="4" />
</Grid>
</ScrollViewer>
</Border>
</Popup>
</Grid>
</UserControl>
绑定值背后的代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MyProg.ViewModels;
namespace MyProg.Views
{
/// <summary>
/// Interaction logic for BitField.xaml
/// </summary>
public partial class BitField : UserControl
{
internal vmBitField m_ViewModel;
public BitField()
{
InitializeComponent();
// Load the viewModel
m_ViewModel = new vmBitField();
LayoutRoot.DataContext = m_ViewModel;
m_ViewModel.PropertyChanged += M_ViewModel_PropertyChanged;
}
private void M_ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Value")
{
if(IntValue != m_ViewModel?.Value)
IntValue = (int)m_ViewModel.Value;
}
}
/// <summary>
/// Gets or sets the value which is displayed
/// </summary>
public int IntValue
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value);}
}
/// <summary>
/// Identified the Value dependency property
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("IntValue", typeof(int), typeof(BitField), new PropertyMetadata(0, OnIntValueSet));
private static void OnIntValueSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((BitField)d).m_ViewModel.Value = (uint?)(int)e.NewValue;
}
}
}
和用户控件视图模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MyProg.MVVM;
namespace MyProg.ViewModels
{
class vmBitField : ViewModelBase
{
#region Members
protected uint m_Value = 0; // 7
#endregion
#region Properties
public uint? Value { get { return m_Value; } set { if (value != m_Value) { m_Value = (uint)value; RaiseEventPropertyChanged("Option1"); RaiseEventPropertyChanged("Option2"); RaiseEventPropertyChanged("Option3"); RaiseEventPropertyChanged("Text"); } } }
public bool Option1 { get {return (Value & 0x1) == 1; } set { if (value != Option1) { m_Value ^= 0x1; RaiseEventPropertyChanged("Option1"); RaiseEventPropertyChanged("Value"); RaiseEventPropertyChanged("Text"); } } }
public bool Option2 { get { return (Value & 0x2) == 1; } set { if (value != Option2) { m_Value ^= 0x2; RaiseEventPropertyChanged("Option2"); RaiseEventPropertyChanged("Value"); RaiseEventPropertyChanged("Text"); } } }
public bool Option3 { get { return (Value & 0x4) == 1; } set { if (value != Option3) { m_Value ^= 0x4; RaiseEventPropertyChanged("Option3"); RaiseEventPropertyChanged("Value"); RaiseEventPropertyChanged("Text"); } } }
public string Text { get { return "Option1:" + Option1 + " Option2:" + Option2 + " Option3:" + Option3; } }
#endregion
#region Contructors
public vmBitField()
{
}
#endregion
#region Methodes
#endregion
}
}
当我在页面或 windows.
中插入此 userControl 时,这可以正常工作<local:LockType IntValue="{Binding Path=ValueAsInt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
现在,我有一个包含很多位域的列表。我想在数据网格中显示所有这些。所以我想将这个 userControl 插入到 dataGrid 中。 这是添加数据网格的代码
<DataGrid Grid.Row="13" Grid.ColumnSpan="3" AutoGenerateColumns="False" Name="dataGrid1" AlternationCount="2"
ItemsSource="{Binding Values}"
CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" CanUserReorderColumns="False" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Id" HeaderStyle="{StaticResource DataGridHeaderCenter}"
CellTemplate="{StaticResource IdText}"/>
<DataGridTemplateColumn Header="Value" Width="400" HeaderStyle="{StaticResource DataGridHeaderCenter}" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local1:LockType IntValue="{Binding Path=ValueAsInt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="CurrentValue"
CellTemplate="{StaticResource CurrentValueText}" />
</DataGrid.Columns>
</DataGrid>
当我的用户控件位于数据网格中时,当我单击切换按钮时,它会正确打开弹出窗口,但随后我无法与弹出窗口中的任何复选框进行交互(选中或取消选中)。
谁能帮我找出为什么弹出窗口中的复选框没有触发事件? 如果需要,我可以添加更多信息。
最好的问候 JM
您必须将弹出窗口的 FocusManager.IsFocusScope 设置为 true。 您还可以通过样式 setter.
设置单元格的 IsFocussable 属性<Style TargetType="{x:Type DataGridCell}"> <Setter Property="Focusable" Value="False"></Setter> </Style>
弹窗不让你编辑任何东西的问题是因为单元格编辑还没有结束。因此,您也可以通过注册 DataGrid_CellEditEnding 活动来执行以下操作。
private static void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.Column.GetType() == typeof(DataGridTemplateColumn))
{
var popup = GetVisualChild<Popup>(e.EditingElement);
if (popup != null && popup.IsOpen)
{
e.Cancel = true;
}
}
}