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;
        }
    }   
}