WPF DataGrid -禁用时保留选择
本文关键字:保留 选择 DataGrid WPF | 更新日期: 2023-09-27 18:01:22
我已经为此挣扎了一段时间了。我在我的应用程序中有一个主/详细信息布局,并且像许多其他人一样,面临着禁用DataGrid时失去其选择的问题。从本质上讲,在从列表中选择一个元素来填充一系列字段之后,用户按"Edit",这将禁用DataGrid并启用所有表单字段。保存数据后,按"保存"按钮将恢复这些操作…非常直截了当。
我在Windows 7上使用。net Framework 4开发VS 2010。
我试过了:
1)基于这篇文章,我尝试在2009年6月版本的WPF工具包中使用DataGrid,但我有同样的反应。2)基于这个WPF CodePlex bug报告,我试图创建一个基于DataGrid的自定义控件,并覆盖OnIsEnabledChanged调用以删除对"UnselectAllCells"的调用,但是没有代码示例,我甚至不能让它触发一次。我试过了:
public class FormMainDataGrid : DataGrid
{
static FormMainDataGrid()
{
IsEnabledProperty.OverrideMetadata(typeof(FormMainDataGrid), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsEnabledChanged)));
}
public FormMainDataGrid() : base() { }
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(CanUserAddRowsProperty);
d.CoerceValue(CanUserDeleteRowsProperty);
//this was added in new version !!!
/*
if (!(bool)(e.NewValue))
{
((DataGrid)d).UnselectAllCells();
}
*/
// Many commands use IsEnabled to determine if they are enabled or not
CommandManager.InvalidateRequerySuggested();
}
}
,但这仍然取消当前选择的行,只要我禁用DataGrid。我试着像这样解释最后的注释(在Codeplex bug报告中):
public class FormMainDataGrid : DataGrid
{
static FormMainDataGrid()
{
}
public static void OverrideStuff()
{
IsEnabledProperty.OverrideMetadata(typeof(FormMainDataGrid), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsEnabledChanged)));
}
public FormMainDataGrid() : base() { }
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(CanUserAddRowsProperty);
d.CoerceValue(CanUserDeleteRowsProperty);
//this was added in new version !!!
/*
if (!(bool)(e.NewValue))
{
((DataGrid)d).UnselectAllCells();
}
*/
// Many commands use IsEnabled to determine if they are enabled or not
CommandManager.InvalidateRequerySuggested();
}
}
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
FormMainDataGrid.OverrideStuff();
base.OnStartup(e);
}
}
,但这甚至不会触发方法的修改版本。
首先,我走的路对吗?考虑到取消选择是由这个方法引起的,我可以完全取代内部调用'OnIsEnabledChanged'为我自己的方法吗?还有别的方法可以解决这个问题吗?或者更具体地说,我如何停止对该方法的基本版本的调用,因为它不是覆盖,因此我不能"不"调用base.OnIsEnabledChanged
?
谢谢!
供将来参考,如果有人遇到同样的问题。
重新设置SelectedValue有很多副作用。
这是覆盖网格上的元数据的正确方法:
public class MyDataGrid : DataGrid
{
static MyDataGrid()
{
IsEnabledProperty.OverrideMetadata(typeof(MyDataGrid), new CustomFrameworkPropertyMetadata(OnIsEnabledChanged));
}
/// <summary>
/// Fixes the issue that the DataGrid's selection is cleared whenever the DataGrid is disabled.
/// Tricky: this issue only happens for 4.0 installations, it is fixed in 4.5 (in-place upgrade) installations.
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(CanUserAddRowsProperty);
d.CoerceValue(CanUserDeleteRowsProperty);
//this is there in 4.0 dlls, not in the in-place upgrade 4.5 dlls.
//if (!(bool)(e.NewValue))
//{
// ((DataGrid)d).UnselectAllCells();
//}
CommandManager.InvalidateRequerySuggested();
}
class CustomFrameworkPropertyMetadata : FrameworkPropertyMetadata
{
public CustomFrameworkPropertyMetadata(PropertyChangedCallback propertyChangedCallback)
: base(propertyChangedCallback)
{
}
protected override void Merge(PropertyMetadata baseMetadata, DependencyProperty dp)
{
// See: http://msdn.microsoft.com/en-us/library/system.windows.propertymetadata.merge.aspx
// See: http://msdn.microsoft.com/en-us/library/ms751554.aspx
// By default, PropertyChangedCallbacks are merged from all owners in the inheritance hierarchy,
// so all callbacks are called whenever the property changes.
var thisPropertyChangedCallback = this.PropertyChangedCallback;
base.Merge(baseMetadata, dp);
// We do NOT want that default behavior here;
// The callback of DataGrid should not be called here - it clears the selection, we don't want that.
// But the callback of UIElement should be called here - it visually disabled the element, we still want that.
if (baseMetadata.PropertyChangedCallback != null)
{
Delegate[] invocationList = baseMetadata.PropertyChangedCallback.GetInvocationList();
PropertyChangedCallback inheritedPropertyChangedCallback = null;
foreach (var invocation in invocationList)
{
if (invocation.Method.DeclaringType == typeof(DataGrid))
{
// Do nothing; don't want the callback from DataGrid that clears the selection.
}
else
{
inheritedPropertyChangedCallback = inheritedPropertyChangedCallback == null
? (PropertyChangedCallback)invocation
: (PropertyChangedCallback)Delegate.Combine(inheritedPropertyChangedCallback, invocation);
}
}
this.PropertyChangedCallback = thisPropertyChangedCallback != null
? (PropertyChangedCallback)Delegate.Combine(inheritedPropertyChangedCallback, thisPropertyChangedCallback)
: inheritedPropertyChangedCallback;
}
}
}
}
请注意,本文中提到的问题只发生在没有安装4.5的4.0安装中。
它在。net 4.5中是"固定的",即使是针对4.0的应用程序也是如此("4.5是一个就地升级"的场景/痛苦)。
问候,
柯恩
我一般不会因为这个原因而禁用控件。我发现它要么崩溃的控制,保持其数据绑定当前,或者如果我必须保持它可见,但不允许任何类型的交互,把部分透明的黑色边框,它通常是崩溃,并成为可见的命令。
当IsHitTestVisible = false时,上下键的问题仍然存在。
所以我最终做的是重新制作自定义控件,像这样:
public class FormMainDataGrid : DataGrid
{
public FormMainDataGrid() : base() {
this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(DataGrid_IsEnabledChanged);
this.SelectionChanged += new SelectionChangedEventHandler(DataGrid_SelectionChanged);
}
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs args)
{
if (this.IsEnabled)
{
_selectedValue = this.SelectedValue;
}
}
private object _selectedValue;
private void DataGrid_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs args)
{
this.Dispatcher.BeginInvoke((Action)(() =>
{
this.SelectedValue = _selectedValue;
}), null);
}
}
这个工作得很好…我必须要小心,因为当控件被禁用时改变SelectedValue会让它偏离轨道。
所以在结论中,我相信你的解决方案是最完整的,但我的允许我保持我的形式的代码精益& &;尽量平均。
谢谢你的帮助!