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 :)
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 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:
I am trying to make a textbox read only using Binding in Windows 8.1 apps. I have tried some code from the internet which does not work.
Can you suggest any simplest way to do it, I am very new to the concept Binding.
XAML
<TextBox x:Name="tbOne" IsReadOnly="{Binding Path=setread, Mode=OneWay}" />
<Button Content="isReadonlyBinding" x:Name="isReadonlyBinding" Click="isReadonlyBinding_Click"></Button>
XAML.CS
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(
"setread",
typeof(bool),
typeof(MainPage),
new PropertyMetadata(false)
);
public bool setread
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
private void isReadonlyBinding_Click(object sender, RoutedEventArgs e)
{
setread = true;
}
try this.
<page X:name="PageName">
IsReadOnly="{Binding ElementName=PageName,Path=setread, Mode=OneWay}"
Implement INotifyPropertyChanged on your code behind. Then modify the property as follows:
private bool _setread;
public bool Setread
{
get { return _setread; }
set {
if(_seatread == value) return;
_setread = value;
RaisePropertyChanged("Setread");
}
}
Give a name to root element like x:Name="root", and bind to Setread with ElementName=page. Note that it is much better to prepare a view model. A view-model-code-behind is just a quick workaround.
I'm currently developping an universal app in C#/XAML with MVVM (not MVVM Light) and I have trouble for the XAML part.
I'd like to display one or another StackPanel when a dependency property changed in my ViewModel. I think the code speaks for itself.
<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- Some content -->
</StackPanel>
<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource InvertBooleanToVisibilityConverter}}">
<!-- Some another content -->
</StackPanel>
And here is the definition of the dependency property.
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
"MyProperty",
typeof (bool),
typeof (MyViewModel),
new PropertyMetadata(true));
public bool MyProperty
{
get { return (bool) GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); OnPropertyChanged(); // Implemented by ReSharper }
}
I guess you figure it out that MyProperty is a boolean that I convert into a Visibility via the converters. So, when MyProperty changed in the ViewModel, the view isn't updated.
I already tried to use the UpdateSourceTrigger property but it's not working. Also, I have no binding error and converters are working fine (I only see one StackPanel at the app launch).
Please keep in mind that I don't want to use the code behind part unless there is no other solution.
Thanks for your help.
I finaly gave up and used the code behind part and it's working fine now.
Are your <StackPanel>s part of some UserControl? If not, why are you using DependencyProperty?
Your implementation is quite off as well.
Lets assume for a minute that this is not part of a Custom Control (correct me -- if I'm wrong, I will rewrite the solution)
So you have a ViewModel and you want to hook up some Properties to it. You really don't need to implement DependencyProperty to do what you want to do, but I will entertain you by implementing it your way.
This is a sample ViewModel with 1 (one) property
using Windows.UI.Xaml;
using System.ComponentModel;
// very simple view model
class MyViewModel : DependencyObject, INotifyPropertyChanged
{
// implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// register
public static DependencyProperty FooterTitleProperty = DependencyProperty.Register("FooterTitle", typeof(string), typeof(MyViewModel),
new PropertyMetadata(string.Empty, OnFooterTitlePropertyChanged));
// the actual property
public string FooterTitle
{
get { return (string) GetValue(FooterTitleProperty); }
set
{
SetValue(FooterTitleProperty, value);
}
}
// this will fire when the property gets change
// it will call the OnPropertyChanged to notify the UI element to update its layout
private static void OnFooterTitlePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
MyViewModel mvm = dependencyObject as MyViewModel;
mvm.OnPropertyChanged("FooterTitle");
}
}
To test out the code we will make a very simple XAML form
<Grid x:Name="ContentPanel">
<StackPanel>
<TextBlock x:Name="tb" Text="{Binding FooterTitle}" FontSize="48"></TextBlock>
<Button Content="Test Property" Click="Button_Click_1"></Button>
</StackPanel>
</Grid>
When you click on the button we will change the Textbox's Text
public sealed partial class MainPage : Page
{
// create the view model
MyViewModel vm = new MyViewModel();
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
// set the text we initial want to display
vm.FooterTitle = "default text";
// set the DataContext of the textbox to the ViewModel
tb.DataContext = vm;
}
// after the button is click we change the TextBox's Text
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// change the text
vm.FooterTitle = "Test Property Has Changed.";
// what happens is the Setter of the Property is called first
// after that happens it launches the `OnFooterTitlePropertyChanged` event
// that we hook up with the Register function.
// `OnFooterTitlePropertyChanged` launches the INotifyPropertyChanged event
// then finally the TextBox will updates it's layout
}
}
At this point you can guess you really don't need the DependencyProperty and say why can't I just launch the INotifyPropertyChanged in the Setter instead? Well you can and it is probably the prefer method.
If all these is part of a UserControl then I can see using a DependencyProperty then in the OnFooterTitlePropertyChanged event you can set the
name_of_textbox.Text = FooterTitle;
I think property name should be given with OnPropertyChanged method, like this;
public bool MyProperty
{
get { return (bool) GetValue(MyPropertyProperty); }
set {
SetValue(MyPropertyProperty, value);
OnPropertyChanged("MyProperty");
}
}
https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.inotifypropertychanged.propertychanged
I'm trying to develop user control with some nested properties that allows to use databinding to set it. For example, I have something like this:
// Top level control
public class MyControl : Control
{
public string TopLevelTestProperty
{
get { return (string)GetValue(TopLevelTestPropertyProperty); }
set { SetValue(TopLevelTestPropertyProperty, value); }
}
public static readonly DependencyProperty TopLevelTestPropertyProperty =
DependencyProperty.Register("TopLevelTestProperty", typeof(string), typeof
(MyControl), new UIPropertyMetadata(""));
// This property contains nested object
public MyNestedType NestedObject
{
get { return (MyNestedType)GetValue(NestedObjectProperty); }
set { SetValue(NestedObjectProperty, value); }
}
public static readonly DependencyProperty NestedObjectProperty =
DependencyProperty.Register("NestedObject", typeof(MyNestedType), typeof
(MyControl), new UIPropertyMetadata(null));
}
// Nested object's type
public class MyNestedType : DependencyObject
{
public string NestedTestProperty
{
get { return (string)GetValue(NestedTestPropertyProperty); }
set { SetValue(NestedTestPropertyProperty, value); }
}
public static readonly DependencyProperty NestedTestPropertyProperty =
DependencyProperty.Register("NestedTestProperty", typeof(string), typeof
(MyNestedType), new UIPropertyMetadata(""));
}
// Sample data context
public class TestDataContext
{
public string Value
{
get
{
return "TEST VALUE!!!";
}
}
}
...
this.DataContext = new TestDataContext();
...
XAML:
<local:mycontrol x:name="myControl" topleveltestproperty="{Binding Value}" >
<local:mycontrol.nestedobject>
<local:mynestedtype x:name="myNestedControl" nestedtestproperty="{Binding Value}" />
</local:mycontrol.nestedobject>
</local:mycontrol>
It works well for property TopLevelTestProperty, but it doesn't work for NestedTestProperty.
It seems that nested bindings do not work. Can anybody help me please? Is there any way to make such binding?
I think that it happens because of my nested object has no any reference to the top level object, so it cannot be resolved using MyControl's DataContext.
H.B. right, nested control does not inherit DataContext from mycontrol.
Tyr out setting it explicitly:
<local:mycontrol x:name="myControl"
topleveltestproperty="{Binding Value}" >
<local:mycontrol.nestedobject>
<local:mynestedtype x:name="myNestedControl"
DataContext="{Binding ElementName=myControl,
Path=DataContext}"
nestedtestproperty="{Binding Value}" />
</local:mycontrol.nestedobject>
</local:mycontrol>