WPF DataGrid - сохранить выбор при отключении - PullRequest
2 голосов
/ 07 июля 2011

Я уже давно борюсь с этим.У меня есть макет Master / Details в моем приложении, и, как и многие другие, я столкнулся с проблемой потери DataGrid при его отключении.По сути, после выбора элемента из списка для заполнения ряда полей пользователь нажимает кнопку «Редактировать», которая отключает DataGrid и включает все поля формы.Нажатие кнопки «Сохранить» вернет эти действия после сохранения данных ... Довольно прямолинейно.

Я работаю на Windows 7 с VS 2010 в .Net Framework 4.

Что я пробовал:
1) На основании этого поста , яЯ пытался использовать DataGrid в версии WPF Toolkit, выпущенной в июне 2009 года, но у меня была такая же реакция.
2) На основе этого отчета об ошибке WPF CodePlex я попытался создать пользовательский элемент управленияоснованный на 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);
    }
}  

, но это даже не запускает модифицированную версию метода.

Во-первых, am-Я иду правильным путем для этого?Учитывая, что отмена выбора вызвана этим методом, могу ли я полностью заменить внутренний вызов OnIsEnabledChanged для моего собственного метода?Есть ли другой способ, которым я мог бы заняться этой проблемой?Или, более конкретно, как я могу остановить вызов базовой версии этого метода, поскольку он не является переопределением, поэтому я не могу 'not' вызвать base.OnIsEnabledChanged?

Большое спасибо!

Ответы [ 3 ]

3 голосов
/ 31 июля 2013

Для дальнейшего использования, если кто-то сталкивается с той же проблемой.
Переустановка SelectedValue имеет много побочных эффектов.
Это правильный способ переопределить метаданные в Grid:

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.0 без установленной версии 4.5.
Это исправлено в .net 4.5, даже для приложений, ориентированных на 4.0 (сценарий «4.5 - это обновление на месте » / страдание ).

С уважением,
Koen

1 голос
/ 08 июля 2011

Обычно я не отключаю элементы управления специально по этой причине. Я обнаружил, что гораздо лучше либо свернуть элемент управления, который поддерживает текущую привязку данных, либо, если я должен держать его видимым, но запретить какое-либо взаимодействие, поместить поверх него частично прозрачную черную границу, которая обычно сворачивается и становится видимой по команде. 1001 *

0 голосов
/ 13 июля 2011

Та же проблема с клавишей Up-Down все еще существует с 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, когда элемент управления отключен, затем сбивает его с толку ...

Итак, в заключение, я считаю, что ваше решение является наиболее полным, но мой позволяет мне сохранять код моей формы как можно более скудным и подлым.

Спасибо за вашу помощь!

...