WPF - ComboBox SelectionChanged => Change TextBox Binding - c#

I have a ComboBox and a TextBox. The TextBox is binding to a "default"-property in my ViewModel.
What I'm trying to accomplish is that when I change the Value in the ComboBox , that the property of the TextBox is changed to another property.
<ComboBox SelectedIndex="0" Name="ComboBox1">
<ComboBoxItem>
Messages1
</ComboBoxItem>
<ComboBoxItem>
Messages2
</ComboBoxItem>
</ComboBox>
<TextBox Text="{Binding Messages1}" IsReadOnly="True" VerticalScrollBarVisibility="Visible" AcceptsReturn="True" Name="LogTextBox" />
I want to change the binding of the TextBox to Messages2. I tried many things but nothing seems to work.
Is there an easy solution?

Assuming you've implemented INotifyPropertyChanged you can do it like this:
Code behind:
public string Message1
{
get { return (string)GetValue(Message1Property); }
set { SetValue(Message1Property, value); }
}
// Using a DependencyProperty as the backing store for Message1. This enables animation, styling, binding, etc...
public static readonly DependencyProperty Message1Property =
DependencyProperty.Register("Message1", typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));
public string Message2
{
get { return (string)GetValue(Message2Property); }
set { SetValue(Message2Property, value); }
}
// Using a DependencyProperty as the backing store for Message2. This enables animation, styling, binding, etc...
public static readonly DependencyProperty Message2Property =
DependencyProperty.Register("Message2", typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));
//an array of properties as combobox.Items
public DependencyProperty[] AllowedProperties
{
get
{
return new DependencyProperty[] { Message1Property, Message2Property };
}
}
//selected property as combobox.selectedItem
DependencyProperty _chosenProperty;
public DependencyProperty ChosenProperty
{
get
{
return _chosenProperty;
}
set
{
_chosenProperty = value;
OnPropertyChanged("ChosenValue");
}
}
//value of the selected property as textbox.text.
public string ChosenValue
{
get
{
return ChosenProperty == null ? string.Empty : (string)GetValue(ChosenProperty);
}
}
XAML:
<ComboBox ItemsSource="{Binding AllowedProperties}"
SelectedItem="{Binding ChosenProperty}"
>
</ComboBox>
<TextBlock Text="{Binding ChosenValue}"/>

That was asked before - the best and most clean solution, yet not that generic - is creating few textboxes, that will be collapsed visible, besides the relevant one. When you update the combobox selected item, then update the visibility bindings of the textboxes.

Related

WPF: DependencyProperty works on TextBlock but not on custom control

Edit: a sample project can be found here.
I am using a ListBox inside my main window, which I later bind to an ObservableCollection. I use both a TextBlock and a custom control which I bind to the same property of the collection. My problem is that the TextBlock gets properly updated, whereas the custom control doesn’t (it gets default constructed but its Text property is never updated by the binding).
<ListBox Name="MyCustomItemList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ItemText}"/>
<local:MyCustomBlock Text="{Binding ItemText}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I implemented MyCustomBlock as a child of System.Windows.Controls.Canvas with a Text dependency property:
public class MyCustomBlock : Canvas
{
public MyCustomBlock() => Text = "<default>";
public MyCustomBlock(string text) => Text = text;
private static void TextChangedCallback(DependencyObject o,
DependencyPropertyChangedEventArgs e)
{
...
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text), typeof(string), typeof(MyCustomBlock),
new FrameworkPropertyMetadata("", TextChangedCallback));
}
Finally, this is the data I bind to the ListBox in the MainWindow constructor:
public class MyCustomItem
{
public MyCustomItem(string text) => ItemText = text;
public string ItemText { get; set; }
}
public MainWindow()
{
InitializeComponent();
var list = new ObservableCollection<MyCustomItem>();
list.Add(new MyCustomItem("Hello"));
list.Add(new MyCustomItem("World"));
MyCustomItemList.ItemsSource = list;
}
Did I forget something in my setup? How come TextBlock.Text is seemingly properly updated but not MyCustomBlock.Text?
Dependency properties can get their value from several sources and so WPF employs a precedence system to determine which value applies. "Local" values (provided using SetValue or SetBinding) will override anything provided by the creating template.
In your case, your setting a "local" value in the constructor (presumably intending it to behave as a default value). A better way to set a default value is by providing it in the PropertyMetadata.
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text), typeof(string), typeof(MyCustomBlock),
new FrameworkPropertyMetadata("<default>", TextChangedCallback));

error on accessing a property of a control in a wpf user control

i have created a wpf user control with a text box and a combo box.
for accessing the text property of the text box i have used the below code
public static readonly DependencyProperty TextBoxTextP = DependencyProperty.Register(
"TextBoxText", typeof(string), typeof(TextBoxUnitConvertor));
public string TextBoxText
{
get { return txtValue.Text; }
set { txtValue.Text = value; }
}
in another project i have used the control and bind the text as below:
<textboxunitconvertor:TextBoxUnitConvertor Name="wDValueControl" TextBoxText="{Binding _FlClass.SWa_SC.Value , RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="161" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top"/>
i am certain that the class that is used for binding is properly working because when i used it to bing with a text box directly in my project it works properly but when i bind it to the text property of textbox in usercontrol it brings null and the binding does not work.
can any one help me?
Your dependency property declaration is wrong. It has to look like shown below, where the getter and setter of the CLR property wrapper call the GetValue and SetValue methods:
public static readonly DependencyProperty TextBoxTextProperty =
DependencyProperty.Register(
"TextBoxText", typeof(string), typeof(TextBoxUnitConvertor));
public string TextBoxText
{
get { return (string)GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
In the XAML of your UserControl, you would bind to the property like this:
<TextBox Text="{Binding TextBoxText,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
If you need to get notified whenever the TextBoxText property changes, you could register a PropertyChangedCallback with PropertyMetadata passed to the Register method:
public static readonly DependencyProperty TextBoxTextProperty =
DependencyProperty.Register(
"TextBoxText", typeof(string), typeof(TextBoxUnitConvertor),
new PropertyMetadata(TextBoxTextPropertyChanged));
private static void TextBoxTextPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
TextBoxUnitConvertor t = (TextBoxUnitConvertor)o;
t.CurrentValue = ...
}
You are not creating the dependency property right. Use this code:
public string TextBoxText
{
get { return (string)GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
public static readonly DependencyProperty TextBoxTextProperty =
DependencyProperty.Register("TextBoxText", typeof(string), typeof(TextBoxUnitConvertor), new PropertyMetadata(""));
Then in your custom control Bind the TextBoxText to the value of txtValue.Text

Automatic updte of ListView items

I am new to WPF Binding. Is there any way the listview automatically update when one of the item in ItemSource modifies its own dependecny property. I was trying it to do with FreezableCollection.
My code is given below and the aim is to update the listbox when the textbox is modified.
MainWindow.xaml
<Grid x:Name="mainDataGrid">
<StackPanel Orientation="Horizontal">
<ListView x:Name="membersListView" ItemsSource="{Binding}" MinWidth="100"/>
<StackPanel>
<TextBox x:Name="selectedItemTextBox" Text="{Binding ElementName=membersListView, Path=SelectedItem.Name, Mode=TwoWay}" MinWidth="200"/>
</StackPanel>
</StackPanel>
</Grid>
MainWindow.cs
public partial class MainWindow : Window
{
ViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
vm.Add(new Model() { Name = "Name1" });
vm.Add(new Model() { Name = "Name2" });
this.DataContext = vm;
}
}
public class Model : Freezable
{
public String Name
{
get { return (String)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public override string ToString()
{
return Name;
}
// Using a DependencyProperty as the backing store for Name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(String), typeof(Model), new PropertyMetadata(""));
protected override Freezable CreateInstanceCore()
{
return new Model();
}
}
public class ViewModel : FreezableCollection<Model>
{
}
Ok,
Right now your ListView is showing the String Representation of your models, That's why you had to override the "ToString()" method... because you couldn't get it to understand to show the Name property.
Now what happens is that your TextBox changes the Name property well but your listbox doesn't know that "Name" property has changed... because it's looking at ToString()
if you set the "DisplayMemberPath" of your ListView to "Name" , it will not look at ToString(), but rather "Name"... like this:
<ListView x:Name="membersListView" ItemsSource="{Binding}" DisplayMemberPath="Name" MinWidth="100"/>
Note that in this mode if you change the Name property using textbox, the textbox won't update the value of "Name" Property instantly until it loses focus, so to fix that change the binding of textbox text to this:
<TextBox x:Name="selectedItemTextBox" Text="{Binding ElementName=membersListView, Path=SelectedItem.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="200"/>
I've added "UpdateSourceTrigger=PropertyChanged" to ensure that as you start changing the text of TextBox, the Name property is updated instantly.
:) hope it helps.

wpf C# update dependency property from another dependency property

I am kind of new to WPF, I have the following scenario. I want to display a value of a 'Pressure Regulator' in the WPF Label, however, the pressure regulator is not necesserily connected. If it is, I display its value, if it's not I'd like to display e.g., "N/A" string.
To model this scenario I created a class 'OutputValues' with two Dependency Properties - PressureRegulatorValue (double type) and HasPressureRegualator (bool type):
public class OutputValues : DependencyObject, INotifyPropertyChanged
{
// ... some stuff
public bool HasPressureRegulator
{
get { return (bool)GetValue(HasPressureRegulatorProperty); }
set { SetValue(HasPressureRegulatorProperty, value); }
}
// Using a DependencyProperty as the backing store for HasPressureRegulator. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasPressureRegulatorProperty =
DependencyProperty.Register("HasPressureRegulator", typeof(bool), typeof(OutputValues), new PropertyMetadata(false, new PropertyChangedCallback(OutputValues.OnHasPressureRegulatorChanged)));
private static void OnHasPressureRegulatorChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// not sure whether to put any code here
}
public double PressureRegulatorValue
{
get { return (double)GetValue(PressureRegulatorValueProperty); }
set { SetValue(PressureRegulatorValueProperty, value); }
}
// Using a DependencyProperty as the backing store for PressureRegulatorValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PressureRegulatorValueProperty =
DependencyProperty.Register("PressureRegulatorValue", typeof(double), typeof(OutputValues), new PropertyMetadata(0.0));
// ... some more stuff
}
Now I want to bind the Label which is in my GUI (it is part of UserControl called OutputValuesViewer) to an instance of this class. When I bind Label to a data source, the data source should be a dependency property I guess, so in OutputValuesViewer UserControl I created a DP:
public OutputValues OutputValues
{
get { return (OutputValues)GetValue(OutputValuesProperty); }
set { SetValue(OutputValuesProperty, value); }
}
// Using a DependencyProperty as the backing store for OutputValues. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OutputValuesProperty =
DependencyProperty.Register("OutputValues", typeof(OutputValues), typeof(OutputValuesViewer));
I initialize this property in the constructor of OutputValuesViewer:
public OutputValuesViewer()
{
InitializeComponent();
this.OutputValues = new OutputValues();
}
Finally my XAML looks like this:
<UserControl x:Class="OperationDescriptionEditor.OutputValuesViewer"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
xmlns:conversion="clr-namespace:OperationDescriptionEditor">
<!-- Unimportant stuff is left out -->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="btvc" />
<conversion:OutputValuesToPressureRegulatorStringConverter x:Key="ovtprsc" />
</UserControl.Resources>
<!-- More stuff -->
<Label Content="{Binding Path=OutputValues, Mode=OneWay, Converter={StaticResource ovtprsc}, UpdateSourceTrigger=PropertyChanged}" Name="lblPressureRegulatorValue" Grid.Row="0" Grid.Column="1" FontSize="9" />
So I bind the label to the OutputValues DP and I implemented a converter which creates the resulting string based on if HasPressureRegulator is true or not and also based on PressureRegulatorValue. I have a sort of a two-level DP. OutputValues is a DP and since it returns an instance of OutputValues class, I can access its two other DPs so:
OutputValues.HasPressureRegulator
OutputValues.PressureRegulatorValue
Now I would expect that if OutputValues.HasPressureRegulator changes, Label would automatically refresh itself. This does not happen though. I tried adding PropertyValueChanged callback to both:
OutputValues.HasPressureRegulator - the callback fires
OutputValues - the callback does not fire
So I'm guessing that Label does not know that it should refresh itself, because OutputValues property (which Label is bound to) does not inform it about a change.
The question is how should HasPressureRegulator DP inform its "parent" OutputValues DP that its value has changed?
As you can see from the code, I tried implementing INotifyPropertyChanged on OutputValues class, but that did not work.
Much thanks!

The databinding of a usercontrol doesn't update the source model

I'm trying to implement a custom textbox which has a placeholder text. The content of the 'FirstName' property of my model appears in the textbox, as intended. The problem I'm having is when I change the text of the textbox, it isn't updated back in the source model. Why is that?
I've tried setting the binding mode to "TwoWay", but it doesn't change anything. Is there something I'm doing wrong?
Edit: Silly me! As it turns out, I had to put Mode="TwoWay" on both bindings, not just the usercontrol's. I'll mark as answered as soon as possible.
Model.cs
public class Student
{
public string FirstName { get; set; }
}
MainWindow.xaml
<grid>
<ui:prettyTextbox Text="{Binding FirstName}" PlaceholderText="#Enter your name">
</grid>
PrettyTextbox.xaml
<UserControl x:Name="prettyTextbox">
<Grid>
<TextBlock Text="{Binding Path=PlaceholderText, ElementName=prettyTextbox}"
Visibility="{Binding Path=Text, ElementName=prettyTextbox, Converter={StaticResource StringLengthToVisibilityConverter}}"/>
<TextBox Text="{Binding Path=Text, ElementName=prettyTextbox, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
PrettyTextbox.xaml.cs
public partial class PrettyTextbox : INotifyPropertyChanged
{
public static readonly DependencyProperty PlaceholderTextProperty =
DependencyProperty.Register("PlaceholderText", typeof (string),
typeof(PrettyTextbox), new FrameworkPropertyMetadata(default(string)));
public string PlaceholderText
{
get { return (string)GetValue(PlaceholderTextProperty); }
set
{
SetValue(PlaceholderTextProperty, value);
OnPropertyChanged();
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string),
typeof(PrettyTextbox), new FrameworkPropertyMetadata(default(string)));
public string Text
{
get { return (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
OnPropertyChanged();
}
}
public PrettyTextbox()
{
InitializeComponent();
}
}
}
You forgot to make the text property bind two way by default, so you need to change this part:
<ui:prettyTextbox Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
or change FrameworkPropertyMetadata of the text property to:
new FrameworkPropertyMetadata
{
DefaultValue = null,
BindsTwoWayByDefault = true
}

Categories