Измените Цвет фона для текстового поля WPF в измененном состоянии

Мне показали это в небольшом количестве кода однажды и спросил, что он сделал:


hexDigit = "0123456789abcdef"[someNybble];

Другой фаворит:


unsigned char bar[100];
unsigned char *foo = bar;
unsigned char blah = 42[foo];
6
задан Dave Clemmer 8 August 2011 в 16:19
поделиться

6 ответов

Просто дважды используйте MultiBinding с одним и тем же свойством, но для одной из привязок укажите Mode = OneTime. Примерно так:

Public Class MVCBackground
    Implements IMultiValueConverter

    Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
        Static unchanged As Brush = Brushes.Blue
        Static changed As Brush = Brushes.Red

        If values.Count = 2 Then
            If values(0).Equals(values(1)) Then
                Return unchanged
            Else
                Return changed
            End If
        Else
            Return unchanged
        End If
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class

И в xaml:

<TextBox Text="{Binding TestText}">
    <TextBox.Background>
        <MultiBinding Converter="{StaticResource BackgroundConverter}">
            <Binding Path="TestText"    />
            <Binding Path="TestText" Mode="OneTime" />
        </MultiBinding>
    </TextBox.Background>
</TextBox>

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

10
ответ дан 8 December 2019 в 12:22
поделиться

Вам нужно будет использовать преобразователь значений (преобразование строкового ввода в цветной вывод), и самое простое решение включает добавление по крайней мере еще одного свойства в вашу EmployeeViewModel. Вам нужно создать какое-то свойство Default или OriginalValue и сравнить с этим. Иначе как узнать, какова была «исходная стоимость»? Вы не можете определить, изменилось ли значение, если нет чего-то, что хранит исходное значение для сравнения.

Итак, привяжите к свойству text и сравните входную строку с исходным значением в модели представления. Если он изменился, верните выделенный цвет фона. Если он совпадает, вернуть нормальный цвет фона. Вам нужно будет использовать мульти-привязку, если вы хотите сравнить FirstName и LastName вместе из одного текстового поля.

Я создал пример, демонстрирующий, как это может работать :

<Window x:Class="TestWpfApplication.Window11"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
Title="Window11" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
    <local:ChangedDefaultColorConverter x:Key="changedDefaultColorConverter"/>
</Window.Resources>
<StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock>Default String:</TextBlock>
        <TextBlock Text="{Binding Path=DefaultString}" Margin="5,0"/>
    </StackPanel>
    <Border BorderThickness="3" CornerRadius="3"
            BorderBrush="{Binding ElementName=textBox, Path=Text, Converter={StaticResource changedDefaultColorConverter}}">
        <TextBox Name="textBox" Text="{Binding Path=DefaultString, Mode=OneTime}"/>
    </Border>
</StackPanel>

А вот код программной части для окна:

/// <summary>
/// Interaction logic for Window11.xaml
/// </summary>
public partial class Window11 : Window
{
    public static string DefaultString
    {
        get { return "John Doe"; }
    }

    public Window11()
    {
        InitializeComponent();
    }
}

Наконец, вот конвертер, который вы используете:

public class ChangedDefaultColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string text = (string)value;
        return (text == Window11.DefaultString) ?
            Brushes.Transparent :
            Brushes.Yellow;
    }

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

И хотя я обернул рамку вокруг TextBox (потому что я думаю, что это выглядит немного лучше) , привязку фона можно выполнить точно так же:

<TextBox Name="textBox" Text="{Binding Path=DefaultString, Mode=OneTime}"
         Background="{Binding ElementName=textBox, Path=Text, Converter={StaticResource changedDefaultColorConverter}}"/>
4
ответ дан 8 December 2019 в 12:22
поделиться

Вы можете добавить к своей модели ViewModel логические свойства, такие как IsFirstNameModified и IsLastNameModified , и использовать триггер для изменения фона, если текстовое поле в соответствии с этими свойствами . Или вы можете привязать Фон к этим свойствам с помощью конвертера, который возвращает Brush из логического значения ...

1
ответ дан 8 December 2019 в 12:22
поделиться

Если вы используете парадигму MVVM, вам следует рассматривать ViewModels как адаптеры между моделью и View.

Не ожидается, что ViewModel будет полностью во всех отношениях не зависит от существования пользовательского интерфейса, но не зависит от какого-либо конкретного пользовательского интерфейса.

Таким образом, ViewModel может (и должен) иметь функциональные возможности как можно большего числа конвертеров. Практический пример здесь:

Требуется ли пользовательскому интерфейсу знать, равен ли текст строке по умолчанию?

Если ответ - да , это достаточная причина для реализации ] IsDefaultString в ViewModel.

public class TextViewModel : ViewModelBase
{
    private string theText;

    public string TheText
    {
        get { return theText; }
        set
        {
            if (value != theText)
            {
                theText = value;
                OnPropertyChanged("TheText");
                OnPropertyChanged("IsTextDefault");
            }
        }
    }

    public bool IsTextDefault
    {
        get
        {
            return GetIsTextDefault(theText);
        }
    }

    private bool GetIsTextDefault(string text)
    {
        //implement here
    }
}

Затем привяжите TextBox следующим образом:

<TextBox x:Name="textBox" Background="White" Text="{Binding Path=TheText, UpdateSourceTrigger=LostFocus}">
    <TextBox.Resources>
        <Style TargetType="TextBox">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsTextDefault}" Value="False">
                    <Setter Property="TextBox.Background" Value="Red"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBox.Resources>
</TextBox>

Это передает текст обратно в ViewModel при потере фокуса TextBox , что вызывает пересчет IsTextDefault . Если вам нужно делать это много раз или для многих свойств, вы даже можете создать базовый класс, например DefaultManagerViewModel .

3
ответ дан 8 December 2019 в 12:22
поделиться

Вариант последнего ответа может быть всегда в измененном состоянии, если значение не является значением по умолчанию.

 <TextBox.Resources>
    <Style TargetType="{x:Type TextBox}">

        <Style.Triggers>
            <Trigger Property="IsLoaded" Value="True">
                <Setter Property="TextBox.Background" Value="Red"/>
            </DataTrigger>
        </Style.Triggers>

        <Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource Self}, Path=Text" Value="DefaultValueHere">
                <Setter Property="TextBox.Background" Value=""/>
            </DataTrigger>
        </Style.Triggers>

    </Style>
</TextBox.Resources>

0
ответ дан 8 December 2019 в 12:22
поделиться

Совершенно иным способом было бы не реализовывать INotifyPropertyChanged и вместо этого переходить от DependencyObject или UIElement

Они реализуют привязку с помощью DependencyProperty Вы можете использовать только один обработчик событий и пользователя e.Property для поиска правильного текстового поля

Я почти уверен, что проверка e.NewValue! = E.OldValue является избыточной, поскольку привязка не должна была изменяться. Я также верю, что может быть способ реализовать привязку, так что dependentcyObject - это текстовое поле, а не ваш объект ...

Отредактируйте, если вы уже наследуете от любого класса WPF (например, control или usercontrol), вы, вероятно, в порядке и не Не нужно переходить на UIElement, поскольку большая часть WPF наследуется от этого класса

Тогда вы можете иметь:

using System.Windows;
namespace YourNameSpace
{
class PersonViewer:UIElement
{

    //DependencyProperty FirstName
    public static readonly DependencyProperty FirstNameProperty =
        DependencyProperty.Register("FirstName", typeof (string), typeof (PersonViewer),
                                    new FrameworkPropertyMetadata("DefaultPersonName", FirstNameChangedCallback));

    public string FirstName {
        set { SetValue(FirstNameProperty, value); }
        get { return (string) GetValue(FirstNameProperty); }
    }

    private static void FirstNameChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) {

        PersonViewer owner = d as PersonViewer;
        if (owner != null) {
            if(e.NewValue != e.OldValue && e.NewValue != "DefaultPersonName" ) {

                //Set Textbox to changed state here

            }
        }

    }

    public void AcceptPersonChanges() {

        //Set Textbox to not changed here

    }

 }
}
1
ответ дан 8 December 2019 в 12:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: