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

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
}

Related

Radio checked function with wpf MVVM

I was struggling to trigger checked function in WPF, but when it comes to code-behind concept it is okay, but I want to get checked button value with MVVM theory. So do you guys have any idea about this? and in the below, there is tried code with code behind theory.
<DataTemplate>
<dc:RadioButton GroupName="DemoRadios1"
Margin="0,0,15,0"
IsEnabled="{Binding RadioIsEnabled}"
IsReadOnly="{Binding RadioIsReadOnly}"
InnerCheckerVisibilityWhenReadOnly="{Binding RadioInnerCheckerVisibilityWhenReadOnly}"
InnerCheckerVerticalAlignment="{Binding RadioInnerCheckerVerticalAlignment}"
IsChecked="{Binding IsChecked}"
Content="{Binding OverridedSettingValueName}"
Checked="RadioButton_Checked"/>
</DataTemplate>
#Code behind function
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
// ... Get RadioButton reference.
var button = sender as RadioButton;
// ... Display button content as title.
var Title = button.Content.ToString();
}
It looks like maybe you have a collection of things you're working with.
RadioButton has a command property, so you could potentially bind a command and commandparameter if this code must be in the window viewmodel.
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
<TextBlock Text="{Binding RowName}"/>
<ItemsControl ItemsSource="{Binding Rows}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Name}"
IsChecked="{Binding IsChecked}"
Command="{Binding DataContext.CheckedCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
GroupName="DemoRadios1"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
My viewmodel:
public class MainWindowViewModel : BindableBase
{
public DelegateCommand<RowVM> CheckedCommand { get; private set; }
public ObservableCollection<RowVM> Rows{ get; set; } = new ObservableCollection<RowVM>(
new List<RowVM>
{
new RowVM{Name="Andrew"},
new RowVM{Name="Bill"},
new RowVM{Name="Carol"},
}
);
private string rowName;
public string RowName
{
get => rowName;
set { SetProperty(ref rowName, value); }
}
public MainWindowViewModel()
{
CheckedCommand = new DelegateCommand<RowVM>((row) =>
{
if(!row.IsChecked)
{
return;
}
RowName = row.Name;
});
}
My RowVM lacks any raising of property changed.
public class RowVM : BindableBase
{
public string Name { get; set; }
public bool IsChecked { get; set; } = false;
}
I'm guessing this datatemplate is used for a collection so there will be an itemscontrol with an itemssource bound to a collection of row viewmodels.
This is somewhat simpler if you can instead have code in the viewmodel for a row.
You already bound IsChecked.
You can therefore just put some code in the set accessor if that bound IsChecked property of your viewmodel:
public class RowViewModel : BindableBase
{
private bool isChecked;
public bool IsChecked
{
get => isChecked;
set
{
isChecked = value;
DoStuff(value);
RaisePropertyChanged();
}
}

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 - ComboBox SelectionChanged => Change TextBox Binding

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.

Use a binding to set the text property of a textbox in a usercontrol - WPF

I have a user control which contains a textbox and have created a get/set in the usercontrol to get/set the text property of the textbox.
public class OpenFileControl : UserControl
{
StackPanel sp;
public TextBox tb;
public string Text { get { return tb.Text; } set { tb.Text = value; } }
I then want to set this value based on a binding later on -
<gX3UserControls:OpenFileControl Text="{Binding Value}" />
But I get the following exception
A 'Binding' cannot be set on the 'Text' property of type 'OpenFileControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
After some investigation It seems Text needs to be a dependency property, but If I do that I cant work out how to pass the value on to the textbox.
How can I fix this.
Consider using something like this.
Control XAML:
<UserControl x:Class="WpfTestBench.OpenFileControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=Filename, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</UserControl>
Control codebehind:
using System.Windows;
namespace WpfTestBench
{
public partial class OpenFileControl
{
public static readonly DependencyProperty FilenameProperty =
DependencyProperty.Register("Filename", typeof (string), typeof (OpenFileControl));
public OpenFileControl()
{
InitializeComponent();
}
public string Filename
{
get { return (string)GetValue(FilenameProperty); }
set { SetValue(FilenameProperty, value); }
}
}
}
Main XAML:
<Window x:Class="WpfTestBench.OpenFileWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfTestBench="clr-namespace:WpfTestBench"
Title="OpenFileWindow" Width="300" SizeToContent="Height">
<StackPanel>
<wpfTestBench:OpenFileControl x:Name="In" Filename="{Binding SelectedFilename, UpdateSourceTrigger=PropertyChanged}" />
<wpfTestBench:OpenFileControl x:Name="Out" Filename="{Binding ElementName=In, Path=Filename}" />
</StackPanel>
</Window>
Main codebehind:
namespace WpfTestBench
{
public partial class OpenFileWindow
{
public OpenFileWindow()
{
InitializeComponent();
DataContext = this;
}
public string SelectedFilename { get; set; }
}
}
Execution result (after typing something in the first control):
If you define the dependency property as the static and the actual property, you can write whatever code behind you want in the body of the property.
public const string TextPropertyName = "Text";
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
TextPropertyName,
typeof(string),
typeof(MyControl),
new UIPropertyMetadata(false));
In the getter and setter you can do something like textBox1.Text = value; but you'd probably be better served using a binding to the property instead. MVVM frameworks make light work of this sort of thing quite often. You might find more success defining a ViewModel (a class with an appropriate FielPath variable for example) and setting the DataContext of the new UserControl to be an instance of the ViewModel class, using Bindings to do the heavy lifting for you.

Binding to DependencyProperty works only with "MyValue" and not "{Binding PropertyHoldingMyValue}"

I am creating a custom "PageHeaderControl" UserControl, with a header property:
public partial class PageHeaderControl: UserControl
{
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header",
typeof(string), typeof(PageHeaderControl),
new PropertyMetadata(""));
public string Header
{
get { return GetValue(HeaderProperty) as string; }
set { SetValue(HeaderProperty, value); }
}
}
In the XAML for that control, I have:
<sdk:Label Content="{Binding Header,Mode=TwoWay}" />
Now for the problem: When I create the control, binding it only works to do this:
<my:PageHeaderControl Header="This is my page header" />
And it does not work to do this, where PageHeader is the property in my ViewModel holding the header value:
<my:PageHeaderControl Header="{Binding PageHeader,Mode=TwoWay}" />
I thought maybe my properties were messed up, but this also works:
<TextBlock Text="{Binding PageHeader,Mode=TwoWay}" />
Any ideas as to what the problem could be!
Thanks so much!!!
Edit:
In my ViewModel, PageHeader is this:
private string _pageHeader = "This is my page header";
public string PageHeader
{
get
{
return _pageHeader;
}
set
{
_pageHeader = value;
RaisePropertyChanged("PageHeader");
}
}
Edit 2:
When I put a breakpoint inside the "get" for my PageHeader property, it does not get hit AT ALL, unless I add in the TextBlock...
If I understand you correctly you're trying to bind a property of an element within your control's XAML markup to the property of the control itself.
If this is the case, see if the following helps you.
PageHeaderControl.xaml:
<UserControl x:Class="TryElementBinding.PageHeaderControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name = "MyControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding Header, ElementName=MyControl}"></TextBlock>
</Grid>
PageHeaderControl.xaml.cs:
public partial class PageHeaderControl : UserControl
{
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(PageHeaderControl), new PropertyMetadata(""));
public string Header
{
get
{
return GetValue(HeaderProperty) as string;
}
set
{
SetValue(HeaderProperty, value);
}
}
public PageHeaderControl()
{
InitializeComponent();
}
}
ViewModel.cs:
public class ViewModel : INotifyPropertyChanged
{
private string _pageHeader = "This is my page header";
public string PageHeader
{
get
{
return _pageHeader;
}
set
{
_pageHeader = value;
PropertyChanged(this, new PropertyChangedEventArgs("PageHeader"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
MainPage.xaml:
<Grid x:Name="LayoutRoot" Background="White">
<my:PageHeaderControl Header="{Binding PageHeader, Mode=TwoWay}"></my:PageHeaderControl>
</Grid>
MainPage.xaml.cs:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
I'm a little bit confused and I think you missed the syntax of Binding inline expression.
after "{Binding" comes Path to your property. Is "PageHeader" is a path to your property?!
I think you mean this:
<my:PageHeader Header="{Binding PageHeader, Mode=TwoWay}" />
<TextBlock Text="{Binding PageHeader, Mode=TwoWay}" />
The problem is that Binding expression only works when you set the value of property using SetValue method and notify the parent DependencyObject that specific property has changed!
You should use a DependencyProperty to have TwoWay Binding on it, OR implement System.ComponentModel.INotifyPropertyChange interface in your class and notify the Binding object manually by calling PropertyChanged event in the interface.
The definition of PageHeader property should be like this:
public static readonly DependencyProperty PageHeaderProperty = DependencyProperty.Register("PageHeader", typeof(string), typeof(YOUROWNER), new PropertyMetadata(""));
public string PageHeader
{
get { return GetValue(PageHeaderProperty) as string; }
set { SetValue(PageHeaderProperty, value); }
}
Cheers

Categories