Обновление MultiBinding на DataGridCell - PullRequest
0 голосов
/ 29 июня 2018

Я интегрирую обнаружение изменения свойств в приложение с помощью MultiBinding и IMultiValueConverter. Поэтому, когда пользователь вносит изменения в «DataGrid», DataGridCell' changes background color. My issue is that when the user saves their work I cannot remove the changed background without a screen flicker. I can do DataContext = null then DataContext = this`, но это вызывает мерцание экрана. Я не могу вызвать привязку обновления для сброса MultiBinding по умолчанию.

В: Как я могу обновить MultiBinding для DataGridCell, как показано ниже?

К сожалению, это не MVVM. Я создал проект, который показывает проблему: https://github.com/jmooney5115/clear-multibinding

Это решение работает для TextBox, но не для DataGridCell:

foreach (TextBox textBox in FindVisualChildren<TextBox>(this))
{
    multiBindingExpression = BindingOperations.GetMultiBindingExpression(textBox, TextBox.BackgroundProperty);
    multiBindingExpression.UpdateTarget();
}

Это мультисвязывание для ячейки сетки данных. Преобразователь с несколькими значениями принимает исходное значение и измененное значение. Если значение изменяется, возвращается значение true, чтобы установить цвет фона на LightBlue. Если false, фон является цветом по умолчанию.

<DataGrid.Columns>
    <DataGridTextColumn Header="Destination Tag name" Binding="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
        <!-- /4185578/problema-pri-smeshivanii-konvertera-multibinding-i-triggera-v-stile -->
        <DataGridTextColumn.CellStyle>
            <Style TargetType="{x:Type DataGridCell}">
                <Style.Triggers>
                    <DataTrigger Value="True">
                        <DataTrigger.Binding>
                            <MultiBinding Converter="{StaticResource BackgroundColorConverterBool}">
                                <Binding Path="Name"    />
                                <Binding Path="Name" Mode="OneTime" />
                            </MultiBinding>
                        </DataTrigger.Binding>
                    </DataTrigger>

                    <Setter Property="Background" Value="LightBlue"></Setter>
                </Style.Triggers>
            </Style>
        </DataGridTextColumn.CellStyle>
    </DataGridTextColumn>
</DataGrid.Columns>

Вот конвертер значений, который я использую:

/// <summary>
/// /1052838/izmenit-tsvet-fona-dlya-tekstovogo-polya-wpf-v-izmenennom-sostoyanii
/// 
/// Property changed and display it on a datagrid.
/// 
/// Boolean Converter
/// </summary>

public class BackgroundColorConverterBool : IMultiValueConverter
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="values"></param>
    /// <param name="targetType"></param>
    /// <param name="parameter"></param>
    /// <param name="culture"></param>
    /// <returns>True is property has changed</returns>
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] is null || values[1] is null) return false;


        if (values.Length == 2)
            if (values[0].Equals(values[1]))
                return false;
            else
                return true;
        else
            return true;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Обновление

Используя решение, помеченное как мой ответ, я смог расширить его, чтобы обобщить метод UpdateState ().

foreach (var prop in this.GetType().GetProperties())
    _memo[prop.Name] = prop.GetValue(this);

1 Ответ

0 голосов
/ 30 июня 2018

Я бы не стал полагаться на режим привязки OneTime и хитрости с очисткой привязки для отслеживания изменений в данных. Вместо этого реализуйте что-то вроде шаблона Memento.

Добавьте этот код, который хранит состояние в классе Item:

private Dictionary<string, object> _memo = new Dictionary<string, object>();
public object this[string key]
{
    get 
    {
        object o;
        _memo.TryGetValue(key, out o);
        return o;
    }
}

public void UpdateState()
{
    _memo["Name"] = Name;
    _memo["Description"] = Description;
    _memo["Alias"] = Alias;
    _memo["Value"] = Value;
    OnPropertyChanged("Item[]");
}

для работы индексатора this[] необходимо переименовать класс (например, в ItemVm), поскольку имя класса и имя члена не могут совпадать, а .NET использует "Item" в качестве имени свойства индексатора.

обратите внимание, что уведомления для индексатора имеют формат "Item[]", и метод VerifyProperty() также должен быть исправлен:

private void VerifyProperty(string propertyName)
{
    if (propertyName == null || propertyName == "Item[]")
        return;

Теперь, чтобы использовать неизмененное значение в окне, выполните привязку к индексатору следующим образом:

<Style TargetType="{x:Type DataGridCell}">
    <Style.Triggers>
        <DataTrigger Value="True">
            <DataTrigger.Binding>
                <MultiBinding Converter="{StaticResource BackgroundColorConverterBool}">
                    <Binding Path="Value"   />
                    <Binding Path="[Value]" />
                </MultiBinding>
            </DataTrigger.Binding>

            <Setter Property="Background" Value="LightBlue"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

сохранение исходного состояния при создании предметов:

for(var i = 0; i < 100; i++)
{
    Items.Add(new ItemVm
    {
        Alias = string.Format("Item {0}", i.ToString()),
        Description = string.Format("Description {0}", i.ToString()),
        Name = string.Format("Name {0}", i.ToString()),
        Value = string.Format("Value {0}", i.ToString())
    });
    Items[i].UpdateState();
}

и сохранить изменения в состоянии по нажатию кнопки:

private void Button_Click(object sender, RoutedEventArgs e)
{
    UIHelper.UpdateDataBindings<Control>(this);

    foreach(var item in Items)
    {
        item.UpdateState();
    }
}
...