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!
Related
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}}"/>
I have a custom class derived from ContentPresenter which has a DepenedencyProperty. However, when I attempt to bind it, nothing happens (Source's getter is not called, content is not updated, no errors are logged in Trace):
public class IsReadOnlyCellPresenter : ContentPresenter
{
[Bindable(true)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(IsReadOnlyCellPresenter), new FrameworkPropertyMetadata(null));
}
xaml:
<local:IsReadOnlyCellPresenter Text="{Binding Path=MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<local:IsReadOnlyCellPresenter.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Text, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</DataTemplate>
</local:IsReadOnlyCellPresenter.ContentTemplate>
</local:IsReadOnlyCellPresenter>
If I use static value (e.g. Text="Foo") it works as expected. If I derive from ContentControl (class IsReadOnlyCellPresenter : ContentControl), the Text binding works but not Mode=TemplatedParent which points to nowhere.
What is the issue with ContentPresenter here? Can ContentPresenter be derived from?
I built a custom control TabularListView based on ListView with a new Property, an ObservableCollection of TabularListViewHeaderColumn.
TabularListView:
class TabularListView : ListView
{
public TabularListView()
{
Columns = new ObservableCollection<TabularListViewHeaderColumn>();
}
public ObservableCollection<TabularListViewHeaderColumn> Columns
{
get { return (ObservableCollection<TabularListViewHeaderColumn>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register("Columns", typeof(ObservableCollection<TabularListViewHeaderColumn>), typeof(TabularListView), new PropertyMetadata(new ObservableCollection<TabularListViewHeaderColumn>()));
}
TabularListViewHeaderColumn:
class TabularListViewHeaderColumn : ContentControl
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(TabularListViewHeaderColumn), new PropertyMetadata(string.Empty));
public string MappingName
{
get { return (string)GetValue(MappingNameProperty); }
set { SetValue(MappingNameProperty, value); }
}
public static readonly DependencyProperty MappingNameProperty =
DependencyProperty.Register("MappingName", typeof(string), typeof(TabularListViewHeaderColumn), new PropertyMetadata(string.Empty));
}
I try to use it like shown below, setting the TabularListViewHeaderColumns directly in XAML:
<Controls:TabularListView>
<Controls:TabularListView.Header>
<ItemsControl>
<DataTemplate>
<Button Content="{Binding Title}" Width="{Binding Width}"
FontWeight="Bold"/>
</DataTemplate>
</ItemsControl>
</Controls:TabularListView.Header>
<Controls:TabularListView.Columns>
<Controls:TabularListViewHeaderColumn Title="Test" MappingName="Inactive" Width="60"/>
<Controls:TabularListViewHeaderColumn Title="Articlenumber" MappingName="DeviceType.ArticleNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Serialnumber" MappingName="SerialNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Inventorynumber" MappingName="EndCustNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Description" MappingName="DeviceType.Description1" Width="300"/>
</Controls:TabularListView.Columns>
</Controls:TabularListView>
Usualy when creating a regular Custom Control like in this example https://social.technet.microsoft.com/wiki/contents/articles/32828.uwp-how-to-create-and-use-custom-control.aspx , I need to bind the respected property to the DependencyProperty, but since I only inherit from ListView and don't have any Xaml, I'm quiet confused how to do so.
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:
So, I am trying to create a UserControl with some DependencyProperties so that I can reuse code. However, some properties are not being update, while others are. What is even weirder is that even for those properties that are being updated the method "set" is not being called at all.
Here is my code:
On the ViewModel:
public ICommand TestCommand = new DelegateCommand(x => MessageBox.Show("Ocorreu Evento"));
public List<string> TestList = new List<string> { "Hello","This","is","a","Test" };
On the View:
<views:CustomizedList Header="Testing"
Items="{Binding TestList}"/>
The User control view:
<StackPanel>
<Label Content="{Binding Header}" />
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="Hello"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
The user control code:
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set
{
MessageBox.Show("New header is " + value);
SetValue(HeaderProperty, value);
}
}
public BindingList<object> Items
{
get { return (BindingList<object>)GetValue(ItemsProperty); }
set {
SetValue(ItemsProperty, value);
Console.WriteLine("Value was set with " + value.Count + " items.");
}
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string),
typeof(ListaCustomizada));
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(BindingList<object>),
typeof(ListaCustomizada));
The Header is showing up, but the Items are not. Not that I added some console prints to check if the methods are being called, but nothing shows up, even for the Header. I am setting the DataContext of the UserControl to be itself.
Any ideas?
EDIT:
Following #Garry Vass sugestion, I added a Callback function. The new code is:
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string),
typeof(ListaCustomizada), new PropertyMetadata("", ChangedCallback));
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(BindingList<object>),
typeof(ListaCustomizada), new PropertyMetadata(new BindingList<object>(), ChangedCallback));
private static void ChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("Dependency property is now " + e.NewValue);
}
With this I can see the value of Header changing, but there's no callback happening for Items.
EDIT:
Changed the TestList to be property instead of field, an it's type to BindingList to be consistent with the data in the user control, still same results.
public BindingList<object> TestList { get; set; }
public ViewModel()
{
TestList = new BindingList<object> { "Hello", "This", "is", "a", "Test" };
}
EDIT:
Testing more I found out that the error comes from the fact that the DP ont eh usercontrol is binded to a DP on the view, which is binded to the VM.
EDIT:
Finally got it to work. Searching a little deeper i found this link at codeproject that explains perfectly how to create usercontrols.
While it may seem counter-intuitive, the WPF binding engine basically ignores the setter and getter in your code and uses its own version which lies deep within the WPF plumbing. So any code you put there to intervene, or in your case, to inspect, will just lie unexecuted.
So why are they there at all? They are helpers at compile time and also give your Xaml something to connect to.
If you want to intervene or inspect what's happening with a Dependency Property, you can declare it like this...
#region MyProperty (DependencyProperty)
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(MainWindow),
new PropertyMetadata(0, ChangedCallback));
private static void ChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("Dependency property is now " + e.NewValue);
}
#endregion
This declares a Dependency Property with a Property Changed call back. WPF will invoke it whenever the property is set.
You will see the changes happening in the call back method by anchoring your debugger where the Console statement is.