Как я могу привязать Foreground к свойству в моей ViewModel?

#c# #wpf #mvvm

#c# #wpf #mvvm

Вопрос:

Я хотел бы привязать свойство foreground текстового блока к свойству в моей ViewModel.

Это не работает :

Редактировать

Вид :

 TextBlock 
Text="{Binding Path=FullName, Mode=OneWay}" 
Foreground="{Binding Path=ForegroundColor}"
Margin="0 5 3 5"
  

Код, лежащий в основе:

 CustomerHeaderViewModel customerHeaderViewModel = new CustomerHeaderViewModel();
customerHeaderViewModel.LoadCustomers();
CustomerHeaderView.DataContext = customerHeaderViewModel;
  

Модель просмотра:

 private System.Windows.Media.Brush _foregroundColor;
_foregroundColor = System.Windows.Media.Brushes.DarkSeaGreen;

public System.Windows.Media.Brush ForegroundColor
{
    get { return _foregroundColor; }
    set { _foregroundColor = value; 
        OnPropertyChanged("ForegroundColor");
    }
}

public CustomerHeaderViewModel()
{
ForegroundColor = System.Windows.Media.Brushes.Red;
}
  

Все остальные свойства (текст и т.д.) правильно привязываются.

Что я делаю не так?

Комментарии:

1. Где вы настраиваете _foregroundColor ? В ctor вашей виртуальной машины?

Ответ №1:

Проверьте, похоже ли ваше решение на это: View:

 <Window x:Class="WpfApplication13.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:vm="clr-namespace:WpfApplication13"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <vm:MainVM/>
    </Window.DataContext>
    <Grid>
        <TextBlock Text="{Binding Path=FullName, Mode=OneWay}" 
                   Foreground="{Binding Path=ForegroundColor}"
                   Margin="0 5 3 5"/>
    </Grid>
</Window>
  

ViewModel:

 public class MainVM : INotifyPropertyChanged
{
    protected void OnPropertyChanged(string porpName)
    {
        var temp = PropertyChanged;
        if (temp != null)
            temp(this, new PropertyChangedEventArgs(porpName));
    }
    public event PropertyChangedEventHandler PropertyChanged;

    private System.Windows.Media.Brush _foregroundColor = System.Windows.Media.Brushes.DarkSeaGreen;

    public string FullName
    {
        get
        {
            return "Hello world";
        }
    }

    public System.Windows.Media.Brush ForegroundColor
    {
        get { return _foregroundColor; }
        set
        {
            _foregroundColor = value;
            OnPropertyChanged("ForegroundColor");
        }
    }
}
  

Запущенное приложение

и помните, что если вы хотите установить новое значение для ForegroundColor в виртуальной машине, вы должны сделать это следующим образом:

 ForegroundColor = System.Windows.Media.Brushes.Red;
  

чтобы вызвать PropertyChangedEvent

В соответствии с новой информацией о вашей проблеме, вы могли бы попробовать это решение:

CustomerHeaderViewModel.cs

 class CustomerHeaderViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Customer> Customers { get; set; }

    public void LoadCustomers()
    {
        ObservableCollection<Customer> customers = new ObservableCollection<Customer>();

        //this is where you would actually call your service
        customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23 });
        customers.Add(new Customer { FirstName = "Jane", LastName = "Smith", NumberOfContracts = 22 });
        customers.Add(new Customer { FirstName = "John", LastName = "Tester", NumberOfContracts = 33 });
        customers.Add(new Customer { FirstName = "Robert", LastName = "Smith", NumberOfContracts = 2 });
        customers.Add(new Customer { FirstName = "Hank", LastName = "Jobs", NumberOfContracts = 5 });

        Customers = customers;
    }
    protected void OnPropertyChanged(string porpName)
    {
        var temp = PropertyChanged;
        if (temp != null)
            temp(this, new PropertyChangedEventArgs(porpName));
    }
    public event PropertyChangedEventHandler PropertyChanged;

    private System.Windows.Media.Brush _foregroundColor = System.Windows.Media.Brushes.DarkSeaGreen;

    public System.Windows.Media.Brush ForegroundColor
    {
        get { return _foregroundColor; }
        set
        {
            _foregroundColor = value;
            OnPropertyChanged("ForegroundColor");
        }
    }
}
  

CustomerHeaderView.xaml

 <UserControl x:Class="TestMvvm444.Views.CustomerHeaderView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="main">
    <Grid>
        <StackPanel HorizontalAlignment="Left">
            <ItemsControl ItemsSource="{Binding Path=Customers}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <StackPanel Orientation="Horizontal">
                                <TextBox
                                Text="{Binding Path=FirstName, Mode=TwoWay}" 
                                Width="100" 
                                Margin="3 5 3 5"/>
                                <TextBox 
                                Text="{Binding Path=LastName, Mode=TwoWay}" 
                                Width="100"
                                Margin="0 5 3 5"/>
                                <TextBlock 
                                Text="{Binding Path=FullName, Mode=OneWay}" 
                                Foreground="{Binding ElementName=main, Path=DataContext.ForegroundColor}"
                                Margin="0 5 3 5"/>
                            </StackPanel>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
</UserControl>
  

В представленном сценарии свойство ForegroundColor находится в CustomerHeaderViewModel.cs, поэтому оно является ценным для всех клиентов. В CustomerHeaderView.xaml я добавил x:Name для UserControl, чтобы иметь возможность ссылаться на DataContext этого элемента. Если вы не хотите использовать x:Name для UserControl, вы можете попробовать это:

 <TextBlock 
    Text="{Binding Path=FullName, Mode=OneWay}"
    Foreground="{Binding RelativeSource={RelativeSource FindAncestor,
    AncestorType={x:Type UserControl}}, Path=DataContext.ForegroundColor}"
    Margin="0 5 3 5"/>
  

Помните, что DataContext этого элемента управления был установлен ранее в MainWindow.cs.

MainWindow.cs

 public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        CustomerHeaderViewModel customerHeaderViewModel = new CustomerHeaderViewModel();
        customerHeaderViewModel.LoadCustomers();
        CustomerHeaderView.DataContext = customerHeaderViewModel;
    }
}
  

Приложение

Комментарии:

1. Большое спасибо PawelSt, все еще возникает та же проблема .. Я добавил дополнительный код выше. Текстовый блок находится внутри пользовательского элемента управления, но, как уже упоминалось, все свойства загружены правильно — только не ForegroundColor .. Спасибо

2. Я не совсем понимаю ваш сценарий и что могло пойти не так, но попробуйте изменить тип ForegroundColor с Brush на SolidColorBrush.

3. Спасибо, Павел, привязка Foreground не работала в моем приложении, поэтому я вернулся к основам, скачал и исправил пример Эдварда Тангуэя : tanguay.info/web/index.php?pg=codeExamplesamp;id=139 чтобы изменить свойство Foreground текстового блока, надеясь исправить мою проблему там и перейти к решению моего приложения..

4. Я улучшил свой ответ, поэтому проверьте, работает ли это новое решение для вас.

5. Большое спасибо, Павел, это отличное решение — большое спасибо. Джо

Ответ №2:

Не рекомендуется помещать элементы пользовательского интерфейса в вашу viewmodel. Ваша модель представления должна инкапсулировать только бизнес-местоположение.

Если вы хотите изменить цвет чего-либо в вашем пользовательском интерфейсе, который зависит от значения вашего текстового поля, лучше использовать триггеры данных в XAML.

Вы можете сделать вот так :

Viewmodel :

 public class MainVm : INotifyPropertyChanged
{
    protected void OnPropertyChanged(string porpName)
    {
        var temp = PropertyChanged;
        if (temp != null)
            temp(this, new PropertyChangedEventArgs(porpName));
    }
    public event PropertyChangedEventHandler PropertyChanged;

    public string FullName
    {
        get { return "Hello world"; }
    }
}
  

XAML (Отредактирован для использования средства выбора цвета, предполагая, что выбранное значение его элемента управления называется «SelectedValue» и что оно возвращает объект Brush)

     <Grid>
    <TextBlock Text="{Binding Path=FullName, Mode=OneWay}" 
               Margin="0 5 3 5" Foreground="{Binding ElementName=colorpicker, Path=SelectedValue}"/>
    <ColorPicker x:Name="colorpicker"/>
</Grid>
  

Комментарии:

1. Спасибо Бруно — Я использую элемент управления выбора цвета для установки ForegroundColor в модели представления, поскольку это может быть любое допустимое шестнадцатеричное число — следовательно, зачем мне нужна логика в модели представления, и я не думаю, что привязка в установщике поддерживается..

2. Если вы можете привязать выбранное значение вашего средства выбора цвета к вашей модели представления, то вы можете напрямую привязать свойство Foreground вашего текстового поля непосредственно к вашему средству выбора цвета 🙂 Я отредактировал свой ответ, чтобы привести вам пример. Нет ничего проще, и больше нет необходимости использовать вашу viewmodel! Таким образом, каждый раз, когда пользователь изменяет значение в color picker, он должен автоматически обновлять ваше текстовое поле foreground, все в привязке XAML 🙂

3. Спасибо за ваше время и предложения, Бруно. В моем приложении есть несколько пользовательских элементов управления, и их viewmodels обновляются через подписку на событие агрегатора событий, поэтому ‘Binding ElementName=colorpicker’ не будет работать …. вот почему я вернулся к основам, чтобы решить эту проблему, загрузив и изменив пример Эдварда Тангуэя : tanguay.info/web/index.php?pg=codeExamplesamp;id=139 чтобы изменить свойство Foreground текстового блока, надеясь исправить мою проблему там и перенести решение в мое приложение