LearningWPFrn
LearningWPFrn

Reputation: 55

MVVM to dynamically change a property

I'm trying to dynamically change this Foreground value when the name "David" is entered:

<RichTextBox x:Name="richTextBox" Height="100" Width="366">
        <FlowDocument>
            <Paragraph>
                <Run Text="{Binding Customer.Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Foreground="{Binding Customer.TColor}"/>
            </Paragraph>
        </FlowDocument>
    </RichTextBox>

Where I'm binding Foreground to TColor. I've changed the color at set to confirm the binding works properly.
Inside of my models my customer class has the following constructor, TColor Property, and OnPropertyChanged method:

public class Customer : INotifyPropertyChanged, IDataErrorInfo
{
    private string name;
    private string tColor;
    private string defaultColor = "#000000";
    /// <summary>
    /// constructor
    /// </summary>
    public Customer(String customerName)
    {
        Name  = customerName;
        TColor = defaultColor;
    }

    public string TColor
    {
        get
        {
            return tColor;
        }
        set
        {
            tColor = defaultColor;
            OnPropertyChanged("TColor");
        }
    }
    /// <summary>
    /// getter and setter
    /// </summary>
    public String Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            OnPropertyChanged("Name");
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

My view models CustomerViewModel class has my ChangeColor method where if its called it should set the color to that corresponding value. A concern I have in this class is how my CanUpdate method is used by my later mentioned ChangeColorCommand Class. Another problem I see but don't know the answer to is if I can use UpdateCommand twice in the same place I'm initializing the other. Yet, the UpdateCommand for CustomerUpdateCommand does what it should.

internal class CustomerViewModel
{
    private Customer customer;
    private CustomerInfoViewModel childViewModel;

    /// <summary>
    /// init new instance of the class
    /// </summary>
    public CustomerViewModel()
    {
        //you could use this for a new Twitter user
        customer = new Customer("David");
        childViewModel = new CustomerInfoViewModel();
        UpdateCommand = new ChangeColorCommand(this);
        UpdateCommand = new CustomerUpdateCommand(this);
    }

    public bool CanUpdate
    {
        get
        {
            if (!(Customer.Name == "David"))
            {
                return false;
            }
            return true;
        }
    }

    /// <summary>
    /// Gets the customer instance
    /// </summary>
    public Customer Customer
    {
        get
        {
            return customer;
        }
    }

    /// <summary>
    /// gets the update command for the view model
    /// </summary>
    public ICommand UpdateCommand
    {
        get;
        private set;
    }

    public void ChangeColor()
    {
        Customer.TColor = "#0000ff"; 
    }
    /// <summary>
    /// saves changes made to the customer instance
    /// </summary>
    public void SaveChanges()
    {
        CustomerInfoView view = new CustomerInfoView()
        {
            DataContext = childViewModel
        };

        childViewModel.Info = Customer.Name + " was updated in the database.";

        view.ShowDialog();
        //Debug.Assert(false, String.Format("{0} was updated.", Customer.Name));
    }
}

A likely place for me to have made a mistake is here in my ChangeColorCommand:

internal class ChangeColorCommand : ICommand
{
    private CustomerViewModel viewModel;

    public ChangeColorCommand(CustomerViewModel viewModel)
    {
        this.viewModel = viewModel;
    }

    #region ICommand members

    public event System.EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {

        return viewModel.CanUpdate;
      //return String.Equals(this, "David")
    }

    public void Execute(object parameter)
    {
        viewModel.ChangeColor();
    }

    #endregion
}

In case I missed a detail about my code the entire project can be found at this repository

Upvotes: 1

Views: 656

Answers (3)

AQuirky
AQuirky

Reputation: 5236

Here is the way to do this...

<RichTextBox x:Name="richTextBox" Height="100" Width="366">
  <FlowDocument>
    <Paragraph  Foreground="{Binding Customer.Color}">
      <Run Text="{Binding Customer.Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
    </Paragraph>
  </FlowDocument>
</RichTextBox>

Where...

    public Brush Color
    {
        get
        {
            return name.Length > 0 && name[0] == 'D' ? Brushes.Red : Brushes.Black;
        }
    }

And here is the key part...

    /// <summary>
    /// getter and setter
    /// </summary>
    public String Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
           PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));
        }
    }

Note the following:

  1. Foreground color is set on paragraph, not run. This doesn't work on run for some reason.
  2. When name is set you need to indicate color is changed. Otherwise color will not be updated.

Upvotes: 2

Eldho
Eldho

Reputation: 8263

How about a data trigger

<Style TargetType="RichTextBox">
      <Setter Property="Foreground"
                  Value="PutYourdefaultColor" />
      <Style.Triggers>
        <DataTrigger Binding="{Binding Customer.Name}"
                     Value="David">
          <Setter Property="Foreground"
                  Value="Green" />
        </DataTrigger>
      </Style.Triggers>
</Style>

Upvotes: 2

Maxim Balaganskiy
Maxim Balaganskiy

Reputation: 1574

Not exactly sure what the program is supposed to do but I see 2 issues:

  • you assign UpdateCommand twice, thus your ChangeColor is never going to work
  • in the TColor property setter you don't use value

Also, there is an issue with binding in RichTextBox. Once you delete or change "David" you split or completely delete the Run. Just try it with another TextBox bound to Name and observe the behaviour

Upvotes: 0

Related Questions