Изменение цвета MultiBinding при изменении свойства - Как прагматично очистить? - PullRequest
0 голосов
/ 26 июня 2018

Редактировать: я создал пример проекта, показывающий, что я сделал, а что нет. https://github.com/jmooney5115/clear-multibinding

У меня есть приложение WPF с элементами управления (текстовое поле, сетка данных и т. Д.). Когда значение изменяется в элементе управления, мне нужно указать его, изменив цвет фона. После сохранения изменений цвет фона необходимо вернуть в неизмененное состояние без перезагрузки элемента управления. Это приложение не MVVM, не судите, я унаследовал его.

У меня отлично работает код для изменения цвета с помощью MultiBinding и преобразователя значений. Проблема в том, что я не могу понять, как сбросить фон после вызова Save () в моем коде. Я попытался сделать DataContext = null, а затем DataContext = это, но элемент управления мигает. Должен быть лучший способ.

В: как я могу сбросить фон в неизмененное состояние без перезагрузки элемента управления?

MultiBinding XAML - это работает путем передачи строки [] в BackgroundColorConverter. Строка [0] является привязкой OneTime. Строка 1 является другой привязкой.

<TextBox.Background>
    <MultiBinding Converter="{StaticResource BackgroundColorConverter}">
        <Binding Path="DeviceObj.Name" />
        <Binding Path="DeviceObj.Name" Mode="OneTime" />
    </MultiBinding>
</TextBox.Background>

BackgroundColorConverter.cs

/// <summary>
/// /1052838/izmenit-tsvet-fona-dlya-tekstovogo-polya-wpf-v-izmenennom-sostoyanii
/// 
/// Property changed
/// </summary>
public class BackgroundColorConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var colorRed = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FFB0E0E6");
        var colorWhite = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("White");

        var unchanged = new SolidColorBrush(colorWhite);
        var changed = new SolidColorBrush(colorRed);

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

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

Обновление

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

<DataGrid.Columns>
    <DataGridTextColumn Header="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>
/// Update the data binding after a save to clear the blue that could be there when
/// a change is detected.
/// </summary>
/// <typeparam name="T">Type to search for</typeparam>
/// <param name="parentDepObj">Parent object we want to reset the binding for their children.</param>
public static void UpdateDataBinding<T>(DependencyObject parentDepObj) where T : DependencyObject
{
    if (parentDepObj != null)
    {
        MultiBindingExpression multiBindingExpression;

        foreach (var control in UIHelper.FindVisualChildren<T>(parentDepObj))
        {
            multiBindingExpression = BindingOperations.GetMultiBindingExpression(control, Control.BackgroundProperty);
            if (multiBindingExpression != null)
                multiBindingExpression.UpdateTarget();
        }
    }
}

Окончательное обновление

Этот вопрос отвечает на вопрос, как использовать MultiBinding для моей цели на DataGridCell: Обновить MultiBinding на DataGridCell

Ответы [ 2 ]

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

IHMO решение MVVM (как предложено Рекшино ), безусловно, лучше, чем решение не-MVVM. Модель представления должна заботиться о трассировке измененных данных.

В любом случае, поскольку вы унаследовали это приложение, вы должны учитывать, сколько времени вам нужно для преобразования всего кода, а иногда это невозможно. Таким образом, в этом случае вы можете принудительно «обновить» каждую отдельную мультисвязку при сохранении ваших данных.

Предположим, это ваш XAML (с двумя или более TextBoxes):

<StackPanel>
    <TextBox Margin="5" Text="{Binding DeviceObj.Name, Mode=TwoWay}">
        <TextBox.Background>
            <MultiBinding Converter="{StaticResource BackgroundColorConverter}">
                <Binding Path="DeviceObj.Name" />
                <Binding Path="DeviceObj.Name" Mode="OneTime" />
            </MultiBinding>
        </TextBox.Background>
    </TextBox>

    <TextBox Margin="5" Text="{Binding DeviceObj.Surname, Mode=TwoWay}">
        <TextBox.Background>
            <MultiBinding Converter="{StaticResource BackgroundColorConverter}">
                <Binding Path="DeviceObj.Surname" />
                <Binding Path="DeviceObj.Surname" Mode="OneTime" />
            </MultiBinding>
        </TextBox.Background>
    </TextBox>

    <Button Content="Save" Click="Button_Click" Margin="5,10,5,10" />
</StackPanel>

Когда вы нажимаете «Сохранить» Button, вы можете заставить MultiBindings обновлять свои собственные цели следующим образом:

private void Button_Click(object sender, RoutedEventArgs e)
{
    MultiBindingExpression multiBindingExpression;

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

Вы можете найти реализацию FindVisualChildren в этом ответе . Я надеюсь, что это может помочь вам.

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

Вы должны вставить свойство bool Saved в DeviceObj и обработать его, если Name или что-то еще было изменено.

ViewModel:

public class Device : INotifyPropertyChanged
{
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (value != _name)
            {
                _name = value;
                Saved = false;
                NotifyPropertyChanged(nameof(Name));
            }
        }
    }
    private string _name;


    public bool Saved
    {
        get
        {
            return _saved;
        }
        set
        {
            if (value != _saved)
            {
                _saved = value;
                NotifyPropertyChanged(nameof(Saved));
            }
        }
    }
    private bool _saved = true;

    public void Save()
    {
        //Saving..
        Saved = true;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged(string info)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
    }
}


Преобразователь:

public class BoolToSolColBrushConverter : IValueConverter
{
    private static SolidColorBrush changedBr = new SolidColorBrush(Colors.Red);
    private static SolidColorBrush unchangedBr = new SolidColorBrush(Colors.Green);
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            if ((bool)value)
            {
                return unchangedBr;
            }

        }
        catch (Exception)
        {
        }
        return changedBr;
    }

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


XAML:

<TextBox Text="{Binding Name}" Background="{Binding Saved, Converter={StaticResiurce BoolToSolColBrushConverter}}" />
...