单击嵌套项时WPF Datagrid不会失去焦点

单击嵌套项时WPF Datagrid不会失去焦点,wpf,datagrid,nested,focus,edit,Wpf,Datagrid,Nested,Focus,Edit,我有一个datagrid,它有一个嵌套另一个datagrid的模板列。当我在主数据网格上编辑人名,然后双击编辑嵌套项时,主项不会失去焦点,因此单元格仍处于编辑模式 我通过为嵌套的datagrid注册GotFocus事件、使用自定义FindAncestor函数在可视化树中查找父datagrid并在主datagrid上调用CancelEdit()来处理它 private void DataGridItem_GotFocus(object sender, RoutedEventArgs e)

我有一个datagrid,它有一个嵌套另一个datagrid的模板列。当我在主数据网格上编辑人名,然后双击编辑嵌套项时,主项不会失去焦点,因此单元格仍处于编辑模式

我通过为嵌套的datagrid注册GotFocus事件、使用自定义FindAncestor函数在可视化树中查找父datagrid并在主datagrid上调用CancelEdit()来处理它

private void DataGridItem_GotFocus(object sender, RoutedEventArgs e)
        {
            var dg =(DataGrid)FindAncestor((DependencyObject)sender, typeof(DataGrid), 2);
            dg.CancelEdit();
        }
那太过分了。是否有一种不同的,也许更像MVVM的方式来解决这个问题

下面是这个简化示例的完整代码

XAML


C#

使用系统;
使用System.Collections.ObjectModel;
使用系统组件模型;
使用System.Linq;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Media;
命名空间datagrid\u焦点
{
公共类项:NotifyObject
{
私有字符串_itemName;
公共字符串ItemName
{
获取{return\u itemName;}
设置{u itemName=value;OnPropertyChanged(“itemName”);}
}
私人双价;
公共双价
{
获取{return\u price;}
设置{u price=value;OnPropertyChanged(“price”);}
}
}
公共类Person:NotifyObject
{
公共ObservableCollection项{get;set;}
私有字符串\u名称;
公共字符串名
{
获取{return\u name;}
设置{u name=value;OnPropertyChanged(“name”);}
}
公共双倍总额
{
获取{returnitems.Sum(i=>i.Price);}
集合{OnPropertyChanged(“Total”);}
}
}
公共抽象类NotifyObject:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
受保护的void OnPropertyChanged(字符串属性)
{
if(PropertyChanged!=null)
PropertyChanged(此,新PropertyChangedEventArgs(property));
}
}
公共类ViewModel:NotifyObject
{
公共ObservableCollection数据收集{get;set;}
公共人物数据收集SelectedItem{get;set;}
公共项ItemsSelectedItem{get;set;}
公共视图模型()
{
DataCollection=新的ObservableCollection
{
新人{Name=“Siegmund Freud”,Items=新观察到的集合{
新项目{ItemName=“Phone”,价格=220},
新项目{ItemName=“Tablet”,价格=350},
} },
新人{Name=“Karl Popper”,Items=new observeCollection{
新项目{ItemName=“泰迪熊豪华版”,价格=2200},
新项目{ItemName=“口袋妖怪”,价格=100}
}}
};
}
}
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
this.DataContext=newviewmodel();
}
公共DependencyObject FindAncestor(DependencyObject当前、类型类型、整数级别)
{
int currentLevel=0;
while(当前!=null)
{
if(current.GetType()==type)
{
currentLevel++;
如果(currentLevel==级别)
{
回流;
}
}
当前=VisualTreeHelper.GetParent(当前);
};
返回null;
}
私有void DataGridItem_GotFocus(对象发送方,RoutedEventTarget e)
{
var dg=(DataGrid)FindAncestor((DependencyObject)发送方,typeof(DataGrid),2);
//dg.CancelEdit();
}
}
}

我不理解逻辑,因此您开始编辑(第一个网格)单元格,当您编辑第二个网格(即第一个网格的编辑)时,您希望取消对第一个网格的编辑。如果是这样,第二个网格的编辑也必须取消。您可以尝试通过绑定Cell.I编辑到视图模型(每个网格有两个属性)来实现您的目标,但我发现这一逻辑并不明确。我希望父数据网格和子数据网格一次编辑一个单元格。“…双击以编辑嵌套项,主数据网格不会失去焦点,以便单元格保持在编辑模式”。我已经理解为您想要的,第一个网格单元不会保持编辑模式。我不理解逻辑,因此您开始编辑(第一个网格)单元,并且在编辑第二个网格(第一个网格的编辑)时,您想要取消对第一个网格的编辑。如果是这样,第二个网格的编辑也必须取消。您可以尝试通过绑定Cell.I编辑到视图模型(每个网格有两个属性)来实现您的目标,但我发现这一逻辑并不明确。我希望父数据网格和子数据网格一次编辑一个单元格。“…双击以编辑嵌套项,主数据网格不会失去焦点,以便单元格保持在编辑模式”。我已经理解为您想要的,第一个网格单元不会保持编辑模式。
<Grid>
        <DataGrid x:Name="DataGrid1"
                  ItemsSource="{Binding DataCollection}"
                  SelectedItem="{Binding DataCollectionSelectedItem}"
                  AutoGenerateColumns="False" 
                  CanUserAddRows="false" >
            <DataGrid.Resources>
                <Style TargetType="DataGridCell">
                    <Setter Property="Background" Value="LightBlue"/>
                </Style>
            </DataGrid.Resources>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="2*" />
                <DataGridTemplateColumn Header="Item/Price" Width="3*">
                    <DataGridTemplateColumn.CellTemplate >
                        <DataTemplate>
                            <DataGrid x:Name="DataGridItem" 
                                      ItemsSource="{Binding Items}"
                                      SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ItemsSelectedItem}"
                                      GotFocus="DataGridItem_GotFocus"
                                      Background="Transparent"
                                      HeadersVisibility="None"
                                      AutoGenerateColumns="False"
                                      CanUserAddRows="false" >
                                <DataGrid.Columns>
                                    <DataGridTextColumn Binding="{Binding ItemName}" Width="*"/>
                                    <DataGridTextColumn Binding="{Binding Price}" Width="50"/>
                                </DataGrid.Columns>
                            </DataGrid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace datagrid_focus
{
    public class Item : NotifyObject
    {
        private string _itemName;
        public string ItemName
        {
            get { return _itemName; }
            set { _itemName = value; OnPropertyChanged("ItemName"); }
        }
        private double _price;
        public double Price
        {
            get { return _price; }
            set { _price = value; OnPropertyChanged("Price"); }
        }
    }

    public class Person : NotifyObject
    {
        public ObservableCollection<Item> Items { get; set; }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; OnPropertyChanged("Name"); }
        }
        public double Total
        {
            get { return Items.Sum(i => i.Price); }
            set { OnPropertyChanged("Total"); }
        }
    }

    public abstract class NotifyObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public class ViewModel : NotifyObject
    {
        public ObservableCollection<Person> DataCollection { get; set; }
        public Person DataCollectionSelectedItem { get; set; }
        public Item ItemsSelectedItem { get; set; }

        public ViewModel()
        {
            DataCollection = new ObservableCollection<Person>
            {
                new Person {Name = "Siegmund Freud", Items = new ObservableCollection<Item> {
                        new Item { ItemName = "Phone", Price = 220 },
                        new Item { ItemName = "Tablet", Price = 350 },
                    } },
                new Person {Name = "Karl Popper", Items = new ObservableCollection<Item> {
                        new Item { ItemName = "Teddy Bear Deluxe", Price = 2200 },
                        new Item { ItemName = "Pokemon", Price = 100 }
                    }}
            };
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new ViewModel();
        }

        public DependencyObject FindAncestor(DependencyObject current, Type type, int levels)
        {
            int currentLevel = 0;
            while (current != null)
            {
                if (current.GetType() == type)
                {
                    currentLevel++;
                    if (currentLevel == levels)
                    {
                        return current;
                    }
                }
                current = VisualTreeHelper.GetParent(current);
            };
            return null;
        }

        private void DataGridItem_GotFocus(object sender, RoutedEventArgs e)
        {
            var dg =(DataGrid)FindAncestor((DependencyObject)sender, typeof(DataGrid), 2);
            //dg.CancelEdit();
        }
    }
}