WPF DataGrid - 禁用时保留选择

时间:2011-07-07 12:57:28

标签: c# wpf datagrid selection

我一直在努力解决这个问题。我在我的应用程序中有一个Master / Details布局,并且像许多其他人一样面临DataGrid在禁用它时失去其选择的问题。 Essencialy,在从列表中选择元素以填充一系列字段后,用户按下“编辑”,禁用DataGrid并启用所有表单的字段。保存数据后,按“保存”按钮将恢复这些操作...漂亮的海峡前进。

我正在使用.Net Framework 4中的VS 2010进行Windows 7开发。

我的尝试:
1)基于this post,我试图在2009年6月版的WPF工具包中使用DataGrid,但我也有同样的反应。
2)基于this WPF CodePlex bug report,我试图创建一个基于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错误报告中),如下所示:

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

非常感谢!

3 个答案:

答案 0 :(得分:3)

如果有人遇到同样的问题,以备将来参考。
重新设置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安装中。
它是固定的&#39;在.net 4.5中,即使是针对4.0的应用程序 (&#34; 4.5是in-place upgrade&#34;场景/ misery)。

问候,
柯恩

答案 1 :(得分:1)

由于这个原因,我通常不会禁用控件。我发现要么折叠控制器以保持其数据绑定电流要好得多,要么我必须保持它可见但不允许任何类型的交互,在它上面放置一个部分透明的黑色边框,通常会折叠并在命令中变得可见。 / p>

答案 2 :(得分:0)

与IsHitTestVisible = false相同的Up-Down键仍存在同样的问题。

所以我最终做的是重新处理自定义控件,如下所示:

    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会使其偏离轨道......

总而言之,我相信你的解决方案是最完整的,但是我允许我将我的表单代码保持为精简和放大。意思是尽可能的。

感谢您的帮助!