我有一个视图和一个视图模型。我有一个DataGrid
,当我选择一行时,TextBox
表示这一行的值。这只是为了显示SelectedItems
是否被触发。
它工作得很好,但如果我切换到另一个选项卡并返回,它就停止工作了。
我的观点:
<Window x:Class="TabControlError.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TabControlError"
xmlns:vm="clr-namespace:TabControlError.ViewModels"
xmlns:dp="clr-namespace:TabControlError.DependencyProperties"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<TabControl>
<TabItem Header="Tab1">
<Grid>
<TextBox Text="{Binding Text}" Width="100" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<DataGrid Margin="0,40,0,0"
ItemsSource="{Binding Numbers}"
SelectedItem="{Binding NumbersSelectedItem}"
dp:DataGridSelectedItemsDependencyProperty.SelectedItems="{Binding NumbersSelectedItems}">
<DataGrid.Columns>
<DataGridTextColumn Header="Number" Binding="{Binding Path=.}" Width="150"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</TabItem>
<TabItem Header="Tab2"/>
</TabControl>
</Grid>
</Window>
我的视图模型库:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace TabControlError
{
class ViewModelBase : INotifyPropertyChanging, INotifyPropertyChanged
{
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Administrative Properties
/// <summary>
/// Whether the view model should ignore property-change events.
/// </summary>
public virtual bool IgnorePropertyChangeEvents { get; set; }
#endregion
#region Public Methods
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the changed property.</param>
public virtual void RaisePropertyChangedEvent([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Raises the PropertyChanging event.
/// </summary>
/// <param name="propertyName">The name of the changing property.</param>
public virtual void RaisePropertyChangingEvent(string propertyName)
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
}
#endregion
}
}
My view model:
using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Text;
namespace TabControlError.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
Numbers.Add(0);
Numbers.Add(1);
Numbers.Add(2);
Numbers.Add(3);
}
private string _text;
public string Text
{
get { return _text; }
set
{
if (_text != value)
{
_text = value;
RaisePropertyChangedEvent();
}
}
}
private ObservableCollection<long> _numbers = new ObservableCollection<long>();
public ObservableCollection<long> Numbers
{
get { return _numbers; }
set
{
if (_numbers != value)
{
_numbers = value;
RaisePropertyChangedEvent();
}
}
}
private long? _numbersSelectedItem;
public long? NumbersSelectedItem
{
get { return _numbersSelectedItem; }
set
{
if (_numbersSelectedItem != value)
{
_numbersSelectedItem = value;
RaisePropertyChangedEvent();
}
}
}
private ObservableCollection<object> _numbersSelectedItems = new ObservableCollection<object>();
public ObservableCollection<object> NumbersSelectedItems
{
get { return _numbersSelectedItems; }
set
{
_numbersSelectedItems = value;
Text = NumbersSelectedItem?.ToString();
RaisePropertyChangedEvent();
}
}
}
}
我的附加属性:
using System.Windows;
using System.Collections;
using System.Windows.Controls;
namespace TabControlError.DependencyProperties
{
class DataGridSelectedItemsDependencyProperty
{
#region SelectedItems
///
/// SelectedItems Attached Dependency Property
///
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached("SelectedItems", typeof(IList),
typeof(DataGridSelectedItemsDependencyProperty),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnSelectedItemsChanged)));
public static IList GetSelectedItems(DependencyObject d)
{
return (IList)d.GetValue(SelectedItemsProperty);
}
public static void SetSelectedItems(DependencyObject d, IList value)
{
d.SetValue(SelectedItemsProperty, value);
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid miDataGrid = (DataGrid)d;
miDataGrid.SelectionChanged += DataGrid_SelectionChanged;
miDataGrid.Unloaded += dataGrid_Unloaded;
}
private static void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataGrid miDatagrid = (DataGrid)sender;
//Esta variable tiene la colección del view model, o la propiedad que hace el binding al dependency property del datagrid.
IList ModelSelectedItems = GetSelectedItems(miDatagrid);
ModelSelectedItems.Clear();
if (miDatagrid.SelectedItems != null)
{
foreach (var item in miDatagrid.SelectedItems)
ModelSelectedItems.Add(item);
}
SetSelectedItems(miDatagrid, ModelSelectedItems);
}
private static void dataGrid_Unloaded(object sender, RoutedEventArgs e)
{
DataGrid miDg = sender as DataGrid;
miDg.SelectionChanged -= DataGrid_SelectionChanged;
miDg.Unloaded -= dataGrid_Unloaded;
}
#endregion
}
}
为什么当我改变选项卡控件中的选项卡时它停止工作?
WPF选项卡控件只有一个内容表示器。当切换选项卡时,将卸载数据网格,并调用Unloaded
事件处理程序,从而删除SelectionChanged
处理程序。当您切换回来时,数据网格再次加载,但SelectedItems
值没有改变,所以您永远不会添加SelectionChanged
处理程序,这就是为什么它永远不会再次调用并且条目不同步的原因。
您可以向数据网格的Loaded
事件添加一个处理程序,以重新附加同步所选项的SelectionChanged
处理程序。例如,应该这样做:
public class DataGridSelectedItemsDependencyProperty
{
// ...your other code.
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid miDataGrid = (DataGrid)d;
if (e.OldValue != null)
{
miDataGrid.SelectionChanged -= DataGrid_SelectionChanged;
miDataGrid.Loaded -= dataGrid_Loaded;
miDataGrid.Unloaded -= dataGrid_Unloaded;
}
if (e.NewValue != null)
{
miDataGrid.SelectionChanged += DataGrid_SelectionChanged;
miDataGrid.Loaded += dataGrid_Loaded;
miDataGrid.Unloaded += dataGrid_Unloaded;
}
}
private static void dataGrid_Loaded(object sender, RoutedEventArgs e)
{
DataGrid miDataGrid = (DataGrid)sender;
miDataGrid.SelectionChanged += DataGrid_SelectionChanged;
}
// ...your other code.
}