UpdateSourceTrigger usage C# WPF - c#

I am currently using this AutoCompleteTextBox in my project: WPFTextBoxAutoComplete
I am binding the TextBox to a List<string> of Employee names. I am doing this like so;
<TextBox
Width="250" Height="50" HorizontalAlignment="Center"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
behaviors:AutoCompleteBehavior.AutoCompleteItemsSource="{Binding Employees}"
/>
What I want the TextBox to do is offer a suggestion when the user types in an Employee's name. However, no suggestion appears at all, which leads me to believe that I am not binding the UpdateSourceTrigger properly.
If I am only binding the behaviour to a List<string> then how does the Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" work when there is no property of the Employee's name? I am a little unsure as to what needs to change to trigger the update source.
The website provides this explanation: Now, every time the "TestText" property of your datacontext is changed, WPFTextBoxAutoComplete will provide you with auto-completion suggestions.
However, I don't believe my DataContext has a "Name" property.
EDIT:
/**** AutoComplete ****/
public static readonly DependencyProperty AutoCompleteTest = DependencyProperty.Register(
"Test", typeof(string), typeof(CompanyManagement), new PropertyMetadata(default(string)));
public string Test
{
get { return (string)GetValue(AutoCompleteTest); }
set { SetValue(AutoCompleteTest, value); }
}
TextBox XAML
<TextBox
Width="250" Height="50" HorizontalAlignment="Center"
Text="{Binding Test, UpdateSourceTrigger=PropertyChanged}"
behaviors:AutoCompleteBehavior.AutoCompleteItemsSource="{Binding Employees}"
/>

You just need a property called Name in your DataContext with change notification (either with DependencyProperty or INotifyPropertyChanged).
With DependencyProperty:
public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
"Name", typeof (string), typeof (WhateverClassYouHave), new PropertyMetadata(default(string)));
public string Name
{
get { return (string) GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
With INotifyPropertyChanged:
public class WhateverClassYouHave: INotifyPropertyChanged
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged(nameof(Name)); // C# 6 feature
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); // C# 6 feature
}
As you type, the Name property will change, notify the behavior about the change and it will offer you the suggestion.
Result:

Related

Custom UserControl DependencyProperty Binding

I created a custom UserControl where among other controls I have the following
<TextBlock Text="{Binding NameUtility}" />
<TextBlock Text="{Binding TotalCost}" "/>
In the code both binding are declared as follows
public static readonly DependencyProperty SetNameUtilityProperty =
DependencyProperty.Register(
nameof(NameUtility),
typeof(string),
typeof(SummaryInfo));
public string NameUtility
{
get { return (string)GetValue(SetNameUtilityProperty); }
set { SetValue(SetNameUtilityProperty, value); }
}
public static readonly DependencyProperty SetTotalCostProperty =
DependencyProperty.Register(
nameof(TotalCost),
typeof(string),
typeof(SummaryInfo));
public string TotalCost
{
get { return (string)GetValue(SetTotalCostProperty); }
set { SetValue(SetTotalCostProperty, value); }
}
The above control is used in another control XAML as
<Utilities:SummaryInfo NameUtility="GAS" TotalCost="{Binding TotalGasEuro}"/>
The binded variable TotalGasEuro is correctly declare as follows
private string _totalGasEuro;
public string TotalGasEuro { get => _totalGasEuro; set { _totalGasEuro = value; NotifyPropertyChanged(); } }
When running the app, GAS shows up, while the binded value, which is updated on runtime, does not. (I removed from the code above graphical portions)
I found out my problem.
Looks like to have a binding as the one I was trying to achieve, you need to specify the relative source.
In my case when calling the custom control from XAML:
<Utilities:SummaryInfo NameUtility="GAS" TotalCost="{Binding TotalGasEuro, RelativeSource={RelativeSource AncestorType=UserControl}}"/>

Binding to property of UserControl doesn't work

I got a custom TextBox which I plan to include in another UserControl, however when setting up the Binding for it, it simply just doesn't bind.
I simplified the code for clarity.
My custom TextBox:
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
partial class CustomTextBox : UserControl
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(String.Empty));
}
This binding works as expected. When using CustomTextBox in another UserControl or Window, I can access the property just as expected.
The following code blocks describe the UserControl that uses CustomTextBox and the corresponding ViewModel with the property I want to bind Text to.
<UserControl>
<UserControl.DataContext>
<vm:MyViewModel />
</UserControl.DataContext>
<local:CustomTextBox Text="{Binding FooBar, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
public class MyViewModel : INotifyPropertyChanged
{
private string _fooBar;
public string FooBar
{
get { return _fooBar = (_fooBar ?? ""); }
set
{
_fooBar = value; OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
My problem occurs exactly when I want to bind the Text property to a ViewModel in another UserControl, it just doesn't work. In this case I tried to bind the Text property to the FooBar property on the MyViewModel class, however changes to the Text property do not get reflected on the FooBar property and vice-versa. However when I hover over the binding in the XAML view, it shows the type of the property, so I don't exactly see what's wrong here.
My best guess is that it has to do with two bindings accessing the same property.
modify DP registration to include FrameworkPropertyMetadataOptions.BindsTwoWayByDefault option
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomTextBox),
new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Binding change not pushed back to gui

I have the following code somewhere (using C# and UWP):
public static readonly DependencyProperty SupportsFineChannelProperty = DependencyProperty.Register(
"SupportsFineChannel", typeof(bool), typeof(ChannelGroupView),
new PropertyMetadata(default(bool)));
public bool SupportsFineChannel {
get { return (bool) GetValue(SupportsFineChannelProperty); }
set { SetValue(SupportsFineChannelProperty, value); }
}
and in my gui I use it with
<TextBox Text="{Binding FineChannel, Mode=TwoWay}" IsEnabled="{Binding SupportsFineChannel, Mode=TwoWay}" />
<CheckBox IsChecked="{Binding SupportsFineChannel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">Supports Fine Channel</CheckBox>
Now I have a (for my understanding) unusual behaviour. If I load the frame while the SupportFineChannel is false, the Textbox is disabled. Check. If I do the same while the bool is true, the Textbox is enabled. Check, binding works.
Now the strange part is that the TextBox won't change its IsEnabled value if I change it trough the checkbox.
With the debugger I confirmed:
The Evaluated Binding value of the CheckBox gets changed
The binding properly propagates to the Variable in the backend which changes as expected
The evaluated textbox binding value though does not take note of the changed DepProp.
I have set the binding to OneWay and TwoWay, both do not alter this behaviour. Why does the DepProp not propagate the change back to the bindings?
EDIT
I just tested it with different properties. Fact is, no dependency property pushes values back to the gui when it changes... srsly?
EDIT2
INotifyPropertyChanged does work though.. Is there some different behaviour with DepProps on UWP that I'm not aware of?
EDIT3
It was easier than I thought to create a repro sample. So nothing crazy with my setups..
For anyone interested, here it is.
In case the link goes down sometime or you don't trust me;
I created a new UWP blank project with target version 10.0, 15063, Min Version 10.0 10586
Create a DataView and a ItemView
public class DataView : DependencyObject {
public static readonly DependencyProperty ItemStuffProperty = DependencyProperty.Register(
"ItemStuff", typeof(ObservableCollection<ItemView>), typeof(DataView),
new PropertyMetadata(default(ObservableCollection<ItemView>)));
public ObservableCollection<ItemView> ItemStuff {
get { return (ObservableCollection<ItemView>) GetValue(ItemStuffProperty); }
set { SetValue(ItemStuffProperty, value); }
}
public DataView() {
ItemStuff = new ObservableCollection<ItemView>();
}
}
public class ItemView : DependencyObject {
public static readonly DependencyProperty SupportsStuffProperty = DependencyProperty.Register(
"SupportsStuff", typeof(bool), typeof(ItemView), new PropertyMetadata(default(bool)));
public bool SupportsStuff {
get { return (bool) GetValue(SupportsStuffProperty); }
set { SetValue(SupportsStuffProperty, value); }
}
public static readonly DependencyProperty TextStuffProperty = DependencyProperty.Register(
"TextStuff", typeof(string), typeof(ItemView), new PropertyMetadata(default(string)));
public string TextStuff {
get { return (string) GetValue(TextStuffProperty); }
set { SetValue(TextStuffProperty, value); }
}
}
Add the DataView and an example Item to your codebehind
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
"Data", typeof(DataView), typeof(MainPage), new PropertyMetadata(default(DataView)));
public DataView Data {
get { return (DataView) GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public MainPage() {
Data=new DataView();
Data.ItemStuff.Add(new ItemView());
this.InitializeComponent();
}
Set your Datacontext to DataContext="{Binding RelativeSource={RelativeSource Self}, Path=Data}"
Add your Listbox
<ListBox ItemsSource="{Binding ItemStuff}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding TextStuff}" IsEnabled="{Binding SupportsStuff, Mode=OneWay}" />
<CheckBox IsChecked="{Binding SupportsStuff, Mode=TwoWay}"> Supports Stuff</CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
???
Profit!

TemplateBinding not working for textbox text

I have a custom control called EnhancedTextBox which is a UserControl that has a TextBox and a Button. To the consumer I want it to mostly look like a TextBox, so I did the following:
<UserControl.Template>
<ControlTemplate TargetType="textBoxes:EnhancedTextBox">
...
<TextBox Text="{TemplateBinding Text}"...
And in EnhancedTextBox I have
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof (String), typeof (EnhancedTextBox));
public String Text
{
get { return (String) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
Yet, when I use it as the following:
<EnhancedTextBox Text="{Binding MyText, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}}" />
Then, MyText is never updated, as well as I inspect EnhancedTextBox.Text and it is null. What am I missing? I have been staring at this for a bit and can't figure out what is wrong. I even thought it might be the fact that I was using the same name, so create a property called Text1 which did not work....
Also of note, if I use a regular TextBox, then this all works. So, I am fairly certain the problem is with the EnhancedTextBox itself
I figured it out after reading this MSDN about TemplateBinding. Specifically,
A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}.
So, I decided to do this explicitly...which would allow me to set the UpdateSourceTrigger (still not sure why it doesn't default to PropertyChanged)
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, UpdateSourceTrigger=PropertyChanged}"....
And, now it is working. TemplateBinding does not even expose these properties....again, not sure why
You are missing the CallBack when you register the property.
Here's a sample code.
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
public void IsSelectedChangedCallback()
{
//actions when property changed
}
private static void OnSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
userControl.IsSelectedChangedCallback();
}
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(MyUserControl), new PropertyMetadata(new PropertyChangedCallback(OnSelectedChanged)));

Create a proxy for a dependency property

I am trying to create a simple dependency property proxy. I made a custom control, it's a file picker, which is made off a textbox (name: "TextBox_FilePath") and a button showing the open file dialog.
As I am making a reusable control I'd like it to have a "SelectedFilePath" property. As the Text property seems to be perfect for my control to be the "SelectedFilePath" property, I'd just like to proxy these dependency property.
The first approach I made was:
public static readonly DependencyProperty SelectedFilePathProperty = TextBox.TextProperty;
public string SelectedFilePath
{
get { return (string) this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}
which worked, but throwed an exception when trying to bind to that property. Then I came off with:
public static readonly DependencyProperty SelectedFilePathProperty =
DependencyProperty.Register("SelectedFilePath", typeof (string), typeof (FilePicker), new PropertyMetadata(default(string)));
public string SelectedFilePath
{
get { return (string) this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}
which does work, but I've got no idea why?! Where did I specify I wanted the text property of the textbox?
What am I missing to simply proxy out that dependency property?
EDIT:
The solution with AddOwner doesn't work too, it throws an Excetion saying "binding can only be applied on a dependency property". Code:
public static readonly DependencyProperty SelectedFilePathProperty =
TextBox.TextProperty.AddOwner(typeof(FilePicker));
public string SelectedFilePath
{
get { return (string)this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}
What don't I understand?
EDIT2:
For everybody else having issues understanding the answer, I've made a little graphic
The first approach does not work because the property is registered only for the TextBox, adding a reference in another class does nothing.
The second one just creates a whole new string property.
If you really want to reuse the TextBox.TextProperty call AddOwner on it.
e.g.
public static readonly DependencyProperty SelectedFilePathProperty =
TextBox.TextProperty.AddOwner(typeof(FilePicker));
(Note that this property is registered as "Text", so you probably should just create a new property with the name you want as you did already. I would also recommend to set metadata flags to bind two-way by default if you want to have the same binding behaviour as TextBox.Text.)
This solution is a little tricky but works.
Given this user control:
<Grid>
<StackPanel>
<WpfApplication1:FilePicker SelectedFilePath ="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding MyProperty}" />
</StackPanel>
</Grid>
And its viewmodel:
public class MainWindowViewModel : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string e)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(e));
}
#endregion
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}
}
XAML for FilePicker control:
<Grid>
<TextBox x:Name="TextBox_FilePath" DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type WpfApplication1:FilePicker}}}" Text="{Binding SelectedFilePath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
CodeBehind for FilePicker control:
public partial class FilePicker : UserControl
{
public FilePicker()
{
InitializeComponent();
}
/* private PROXY DP*/
private static readonly DependencyProperty TextProperty =
TextBox.TextProperty.AddOwner(typeof(FilePicker));
/* public DP that will fire getter/setter for private DP */
public static readonly DependencyProperty SelectedFilePathProperty =
DependencyProperty.Register("SelectedFilePath", typeof(string), typeof(FilePicker), new PropertyMetadata(default(string)));
public string SelectedFilePath
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
Works like a charm.
As I had issues understanding H.B.s answer I made a little graphic which helped me to understand what's going on under the hood. Here it is;
Maybe it helps someone else :)

Categories

Resources