Search code examples
c#.netwpfdata-bindingmultibinding

Notify and update a source through Multibinding with using StringFormat in WPF


Please analyze following code snippet:

<TextBox x:Name="TxtBox_CommandInfo"  Style="{DynamicResource MetroTextBox}"
    IsReadOnly="True" Controls:TextBoxHelper.Watermark="This is a textbox" 
    HorizontalAlignment="Left" Margin="0,236,0,0" TextWrapping="Wrap"
    VerticalAlignment="Top" Height="154" Width="780" 
    ScrollViewer.HorizontalScrollBarVisibility="Auto" 
    ScrollViewer.VerticalScrollBarVisibility="Auto">
  <TextBox.Text>
    <MultiBinding StringFormat="{}{0} {1} {2}" NotifyOnTargetUpdated="True">
      <Binding  Path="A" />
      <Binding  Path="B" />
      <Binding  Path="C"/>
      <Binding Path="D" Mode="TwoWay" NotifyOnTargetUpdated="True"
          UpdateSourceTrigger="PropertyChanged"/>
    </MultiBinding>
  </TextBox.Text>
</TextBox>

Simply as you can seeTxtBox_CommandInfo textbox text(Target) value will be modified/formatted with only by A,B,C , but the updated text should be notified to D via binding.Model as below:

A,B,C => [TexBox.Text] => D

Problem is respective source D in C# side won't get notified and updated when TxtBox_CommandInfo.Text is getting changed.

  1. Without any Converters how to achieve a solution to this issue?
  2. Did I mis-configured the Multibinding setting ?

It really requires StringFormat feature,therefore without injecting conversion do we have any alternatives with StringFormat?


Solution

  • I am still confused. Don't know exactly what you mean:

    What I tried to explain in the comments, here is a solution for that.

    XAML:

     <Grid>
        <StackPanel>
            <ComboBox ItemsSource="{Binding Path=MyTexts}" SelectedItem="{Binding Path=A}" Margin="10"/>
            <ComboBox ItemsSource="{Binding Path=MyTexts}" SelectedItem="{Binding Path=B}" Margin="10"/>
            <ComboBox ItemsSource="{Binding Path=MyTexts}" SelectedItem="{Binding Path=C}" Margin="10"/>
    
            <TextBox Height="80" Margin="10" Text="{Binding Path=D, Mode=OneWay}" IsReadOnly="True" AcceptsReturn="True" TextWrapping="Wrap">
            </TextBox>
        </StackPanel>
    </Grid>
    

    C#: (ViewModel)

     public class MyViewModel : INotifyPropertyChanged
    {
        private string _a;
        private string _b;
        private string _c;
    
        public string A
        {
            get => _a;
            set
            {
                _a = value;
                OnPropertyChanged(nameof(A));
                OnPropertyChanged(nameof(D));
            }
        }
        public string B
        {
            get => _b;
            set
            {
                _b = value;
                OnPropertyChanged(nameof(B));
                OnPropertyChanged(nameof(D));
            }
        }
        public string C
        {
            get => _c;
            set
            {
                _c = value;
                OnPropertyChanged(nameof(C));
                OnPropertyChanged(nameof(D));
            }
        }
        public string D => $"{A} {B} {C}";
    
        public List<string> MyTexts { get; }
    
        public MyViewModel()
        {
            MyTexts = new List<string>() { "FHAKWEFJ AWKEEF AWEKF LAEWKF LAWEF", "AAAAAAAAAAAAAAAAAAAAAA", "BBBBBBBBBBBBBBBBBBBBBBBBB", "EEEEEEEEEEEEEEEEEEEEEEEEE", "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG" };
        }
    
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        #endregion
    }
    

    No hacks, no magic and no events. Plain MVVM

    enter image description here

    EDIT: I guess this is what you want (only showing modifications to above code):

    <TextBox Height="80" Margin="10" TextChanged="TextBoxBase_OnTextChanged" IsReadOnly="True" AcceptsReturn="True" TextWrapping="Wrap">
       <TextBox.Text>
                    <MultiBinding StringFormat="{}{0} {1} {2}" NotifyOnTargetUpdated="True" Mode="OneWay">
                        <Binding  Path="A" Mode="OneWay"/>
                        <Binding  Path="B"  Mode="OneWay"/>
                        <Binding  Path="C" Mode="OneWay"/>
                    </MultiBinding>
                </TextBox.Text>
     </TextBox>
    

    C#:

    private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
    {
        if (this.DataContext is MyViewModel dataContext)
            dataContext.D = ((TextBox) sender).Text;
    }
    

    C# (VM):

        private string _d;
    
        public string D
        {
            get => _d;
            set
            {
                _d = value;
                OnPropertyChanged(nameof(D));
            }
        }