I have problem with binding to dependency property of my new control.
I decided to write some tests to examine this issue.
Binding from TextBox.Text to another TextBox.Text
XAML code:
<TextBox Name="Test" Text="{Binding ElementName=Test2, Path=Text, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="Test2" Grid.Row="2" />
The result is good - when I writing something in first TextBox -> second TextBox is updating (conversely too).
I created new control -> for example "SuperTextBox" with dependency property "SuperValue".
Control XAML code:
<UserControl x:Class="WpfApplication2.SuperTextBox"
...
Name="Root">
<TextBox Text="{Binding SuperValue, ElementName=Root, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
Code behind:
public partial class SuperTextBox : UserControl
{
public SuperTextBox()
{
InitializeComponent();
}
public static readonly DependencyProperty SuperValueProperty = DependencyProperty.Register(
"SuperValue",
typeof(string),
typeof(SuperTextBox),
new FrameworkPropertyMetadata(string.Empty)
);
public string SuperValue
{
get { return (string)GetValue(SuperValueProperty); }
set { SetValue(SuperValueProperty, value); }
}
}
Ok, and now tests!
Binding from TextBox.Text to SuperTextBox.SuperValue
<TextBox x:Name="Test1" Text="{Binding ElementName=Test2, Path=SuperValue, UpdateSourceTrigger=PropertyChanged}" />
<local:SuperTextBox x:Name="Test2" Grid.Row="2"/>
Test is correct too!
When I writing something in TextBox, SuperTextBox is updating.
When i writing in SuperTextBox, TextBox is updating.
All is ok!
Now a problem:
Binding from SuperTextBox.SuperValue to TextBox.Text
<TextBox x:Name="Test1"/>
<local:SuperTextBox x:Name="Test2" SuperValue="{Binding ElementName=Test1, Path=Text, UpdateSourceTrigger=PropertyChanged}" Grid.Row="2"/>
In this case, when I writing something in SuperTextBox, TextBox is not updating!
How can I fix this?
PS: Question is very very long, I am sorry for that, but i tried exactly describe my problem.
The reason why one works and the other doesn't is because the Text dependency property of TextBox is defined to bind TwoWay by default, while your dependency property SuperValue isn't. You need to use TwoWay-binding if you want the destination to update the source in addition to the source updating the destination.
To fix this, you can add FrameworkPropertyMetadataOptions.BindsTwoWayByDefault to SuperValue's metadata like so:
public static readonly DependencyProperty SuperValueProperty = DependencyProperty.Register(
"SuperValue",
typeof(string),
typeof(SuperTextBox),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
Change binding mode into TwoWay.
Since in first two cases Test1 knows when it needs to update itself but not in third case. Only Test2 knows that when it should update in third case. That's why TwoWay mode is required in third case.
EDIT
First case is working since behind the scenes,xaml hook to
AddValueChanged event exposed by the PropertyDescriptor. For the
reason it's working refer to this link here.
Related
My program has a custom DependencyObject to which I bind values of another DependencyObject which are set in code:
<TabControl
Grid.Column="1"
Grid.Row="1">
<TabItem
Header="XML">
<TextBox
Text="{Binding Asset.Xml, ElementName=window}"
IsReadOnly="True" />
</TabItem>
<TabItem
Header="Texture">
<we:DXImage>
<we:DXImage.Renderer>
<we:TextureRenderer
Source="{Binding Asset.Image, ElementName=window}" />
</we:DXImage.Renderer>
</we:DXImage>
</TabItem>
</TabControl>
The TextBox binding to Asset.Xml works flawlessly, also if I replace the xaml of the second item with a TextBox it also displays the content of Asset.Image (a path to an image of type string).
The Source Property of the renderer looks like this:
private static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(TextureRenderer),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, SourceChanged));
public string Source
{
get { return (string)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
private static void SourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// Do stuff
}
However the SourceChanged event is never called.
I have updated the project on GitHub:
https://github.com/Qibbi/WrathEd/tree/master/WrathEd2
the xaml code is located in the WrathEd2 project while the DXImage, Renderer, and other support classes are in WrathEd.Windows
The current MainWindow is a mess code behind wise atm, I plan to refactor it into appropriate parts when finishing the project.
The problem is that your we:TextureRenderer is not a part of the VisualTree (as it is inside a property). Therefore, the binding cannot find the source Element.
According to ElementName Binding is failing, you can use
Source={x:Reference window}
instead of ElementName=window.
According to the MSDN documentation here
You need to set NotifyOnSourceUpdated to true for your binding
I'm new to Windows 8.1 development, XAML, and C#, so if this question is rudimentary, please forgive me.
I've got a <Page> in my app that contains a <ListView>, like so:
<ListView ItemsSource="{Binding Mode=TwoWay}" x:Name="ListView_Statistical">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Width="100" Margin="10,20">
<Run Text="X/Y " />
<!--<Run Text="{Binding Source={StaticResource ThisPage}, Path=i}" />-->
</TextBlock>
<TextBox HorizontalAlignment="Left" Text="{Binding xVal}" PlaceholderText="X" InputScope="Number" FontSize="28" Width="100" Margin="0,10,10,10" />
<TextBox HorizontalAlignment="Left" Text="{Binding yVal}" PlaceholderText="Y" InputScope="Number" FontSize="28" Width="100" Margin="0,10,10,10" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the Code Behind, I set up its DataContext like so:
ListView_Statistical.DataContext = this.statisticalPoints;
this.statisticalPoints is defined as such:
public ObservableCollection<StatisticalPoint> statisticalPoints
{
get { return (ObservableCollection<StatisticalPoint>)GetValue(statisticalPointsProperty); }
set {
SetValue(statisticalPointsProperty, value);
NotifyPropertyChanged("statisticalPoints");
}
}
// Using a DependencyProperty as the backing store for statisticalPoints. This enables animation, styling, binding, etc...
public static readonly DependencyProperty statisticalPointsProperty =
DependencyProperty.Register("statisticalPoints", typeof(ObservableCollection<StatisticalPoint>), typeof(EnterCalc), new PropertyMetadata(0));
I'm not sure if making this a DependencyProperty is necessary, or if making it follow INotifyPropertyChanged is necessary, but they don't seem to hurt.
Anyway, so in my constructor, I add a bunch of stuff to my statisticalPoints:
this.statisticalPoints = new ObservableCollection<StatisticalPoint>();
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 1.0, yVal = 2.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 33.0, yVal = 44.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 555.0, yVal = 666.0 });
this.statisticalPoints.Add(new StatisticalPoint() { xVal = 0.7, yVal = 0.8 });
And when I load up the page, I do indeed see five rows in my ListView, populated as defined in my initialization of this.statisticalPoints.
The part I'm having trouble with is this:
I change the first value in the first <TextBox> in the ListView, then hit my save button... but ListView.Items doesn't have my change reflected, and I can't figure out how to look at the <TextBox> itself.
What I really want to do is have this bunch of statistical points modifiable by my user and be able to save their changes. To do that, I feel like I need to read the value in the <TextBox>es, but I can't figure out how to do that.
Alternatively, if the 'right way' to do this is to keep the data in this.statisticalPoints up-to-date when changes are made in the <TextBox>es, then I thought that a TwoWay binding Mode would do it, but neither ListView.Items nor this.statisticalPoints is changed when I make changes in the <TextBox>.
I do not have event handlers set up in those <TextBox> elements, as you can see, but do I need them, or am I missing something obvious?
Thanks in advance for any help you can give me!
To solve your initial problem, make the binding for each text box Mode="TwoWay". For reasons beyond my comprehension, the mode is default OneWay on pretty much everything in Windows Store Apps.
Making the ItemsSource binding two way does next to nothing, as the UI isn't changing the collection itself (by changing, I mean completely replacing). To iterate over your collection, just iterate over this.statisticalPoints and it will have the current data.
Now, you have a ton of other misconceptions so to try and run through them:
You never showed your save button, but the bindings either update your source or they don't. A save button is usually used to persist changes from the view model to the model.
Speaking of view models, you don't appear to have one. You shouldn't be directly setting the data context of controls, and certainly shouldn't have so much in code-behind. Create a proper view model object for your page, and bind ItemsSource to a public property of that view model.
NotifyPropertyChanged on a collection usually is unnecessary unless you are replacing the collection in code.
Having it won't hurt though, except, the setter of the backing property of a DependencyProperty (DP) is never called by the framework, so putting it there is just weird
And you don't really need a DP at all. DPs are there so a parent control can bind data to your special user control. Until you are using user controls, and really understand how DPs work, you shouldn't need to use them.
I have a GridView that has several buttons. One of them is defined by the following template:
<DataTemplate x:Name="SubjectItemTemplate">
<Canvas Width="340" Height="170" VerticalAlignment="Top">
<Controls:ThreeImageButton HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0"
NormalStateImageSource="{Binding NormalImage}"
HoverStateImageSource="{Binding HoverImage}"
PressedStateImageSource="{Binding PressedImage}" Command="{Binding Path=NavigateToUnitsPage}"
CommandParameter="{Binding}" Canvas.Left="0" Canvas.Top="0">
</Controls:ThreeImageButton>
</Canvas>
</DataTemplate>
Now I have a custom control as you can see, called ThreeImageButton. The button works fine when I use it on its own. But when I have it in the DataTemplate it won't bind properties to the code behind.
Right now, I have
x:Name="MyThreeImageButton"
in the custom button definition. And I connect to the code-behind like this:
<TextBlock Text="{Binding ElementName=MyThreeImageButton, Path=NormalStateImageSource}"/>
(This is just a test to display the text, in the actual code I would assign an image source to another property that is referred to by an element).
Right now, nothing is displayed in the TextBlock. What is the correct binding syntax I'm supposed to use to reach my properties?
Thanks!
Edit: I am setting the variable in the InitializeComponent function and I am using SetValue on the DependencyProperty.
Edit: Let me add the following information to be more clear
Scenario I:
In DataTemplate for GridView:
<UserControl CustomParameter="Literal Text">
In UserControl:
<TextBlock Text="{Binding CustomParameter}">
in UserControl .cs: this.DataContext = this
works!
Scenario II:
In DataTemplate for GridView:
<UserControl CustomParameter="{Binding ValueFromDataItem">
In UserControl:
<TextBlock Text="{Binding CustomParameter}">
in UserControl .cs: this.DataContext = this
nope!
I see,
So setting up a two-way binding to a custom property in a user control can be tricky because a user control cannot bind to a CLR property. Not only that but setting the data context on a user control has an unexpected effect on the binding inside it.
You can solve these problems with a little slight of code. Basically back your CLR properties with dependency properties and set the data context on a child element instead of the root user control.
Take a look at this sample. Let's pretend you have the following MainPage. That MainPage will eventually use our custom user control. So let's set the stage.
Here's the code-behind:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.DataContext = new /* your view model */
{
Title = Guid.NewGuid().ToString(),
};
}
}
In the code above I am simulating a complex view model with a simple anonymous class. It would be silly for you to implement your own like this, but at the same time it is silly for me to build a simple sample with the complete scaffolding. I bring this up only so it does not confuse you - as it could look like I am suggesting this approach in prod.
Here's the XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<local:MyUserControl Text="{Binding Title}" />
</Grid>
In the XAML above, there is absolutely nothing special. I already have reference to the user control in the local namespace and I simply declare it here.
Okay, now that we have a consumer of the control, it's worth pointing out that in testing developers can mistakenly think that their binding is working because they test with literal values. Literal values bind fine. It's binding from the underlying view model that hick-ups.
Let's say another thing, some developers tend to avoid dependency properties because the require a little more typing. People remember that [kbd]propdp[/kbd] is a handy Visual Studio snippet that stubs out a dependency property for you.
Take a look at this user control. It has two controls, a TextBox and a TextBlock which are there to demonstrate the OneWay and TwoWay functionality of this binding approach. We also implement INotifyPropertyChanged on the user control. For the most part, adding a view model in the case of a user control is overkill because the user control already acts like a view model. It's up to the developer, but it seems dumb to me.
Here's the code behind:
public sealed partial class MyUserControl : UserControl, INotifyPropertyChanged
{
public MyUserControl()
{
this.InitializeComponent();
}
// text property
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValueDp(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl), null);
// bindable
public event PropertyChangedEventHandler PropertyChanged;
void SetValueDp(DependencyProperty property, object value,
[System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
SetValue(property, value);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In the ode above, I have create a "Text" property and backed it with a dependency property. For a matter of reuse I have also implemented SetValueDp() which could be used again and again if I had more than a single property. Even though this demo has but one, I wanted to include this because the repetitive logic should certainly be abstracted out like this.
Here's the XAML:
<Grid Background="Black" DataContext="{Binding ElementName=userControl}">
<StackPanel>
<TextBox Text="{Binding Text, Mode=TwoWay}"
MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
<TextBlock Text="{Binding Text}"
MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
</StackPanel>
</Grid>
In the XAML above, I do nothing special insofar as binding. The syntax simply binds to the Text property using the Mode appropriate to the control. Just like you would do normally. However, what's worth noticing is that the DataContext is NOT set on the user control. Instead, it is set on the Grid. As a point of fact, any control in the tree other than the user control could be used like this. Just don't set the data context of the user control.
That is it by the way.
I have tested it to make sure it works. Demonstrating both one and two way binding is pretty handy here. I might even turn this into a blog in case other developers want to find it and don't discover this question. Thanks for your question!
Best of luck!
As the comments alluded to, your DataTemplate is placing the datacontext of the items to whatever object you are adding to your list. This is not the same as the surrounding user control's data context. If you want to reference that datacontext's commands, do the following in the DataTemplate's bindings:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.NormalImage}
What this is saying is to go out and find the user control ancestor and use its datacontext and then look for the NormalImage property. If you run into problems, check your output window for binding errors. It is very helpful in finding binding problems.
I am working with WPF and using data binding.
I would like to make a UserControl which has a property that could be used for data binding.
Also, I want to update some other property in the UserControl if the property changed.
For example,
public class MyControl : UserControl
{
....
....
....
....
public ViewStyles CurrentView
{
get { return (ViewStyles)GetValue(CurrentViewProperty); }
set
{
SetValue(CurrentViewProperty, value);
UpdateView();
}
}
public static readonly DependencyProperty CurrentViewProperty = DependencyProperty.Register("CurrentView", typeof(ViewStyles), typeof(ComboView));
....
.....
.....
.....
}
Problems comes:
A ViewModel is used and in which, there is a property ViewStyle which binded to the CurrentView in the above.
Another control combobox is also data-binded with ViewStyle in the ViewModel.
Actually, I want to use a combobox to choose the different view of my control. How to make it possible in MVVM?
I tried the above method. However, the UI (the different ViewStyles of MyControl) didn't change. It only change when I click on it using the mouse.
Thank you.
XAML: (MyControl)
<Views:MyControl Grid.Column="1" Grid.Row="1" Height="505" HorizontalAlignment="Left" Margin="2,0,0,0" Name="comboView1" VerticalAlignment="Top" Width="983"
ViewStyle="{Binding Path=CurrentView}" BorderThickness="5" BorderBrush="Black" ItemsSource="{Binding Path=Images}"
SelectedIndex="{Binding Path=CurrentIndex}" Foreground="White"
</Views:MyControl>
XAML: (ComboBox)
<ComboBox Margin="0,3,1,0" Width="178" HorizontalAlignment="Right" Name="ViewDDBox" FontSize="13" Foreground="#FFF6F3F3" Background="#FF444444"
BorderThickness="2" Height="23" VerticalAlignment="Top" Grid.Column="1"
ItemsSource="{Binding Path=ViewTypes}" IsEnabled="True" SelectedValue="{Binding Path=CurrentView, Mode=TwoWay}">
</ComboBox>
It is supposed that the view (some UI effect) will be changed of MyControl after choosing in the Combobox. But now, it only change when I click on MyControl using mouse.
The UpdateView() in your CurrentView property setter raises a HUGE red flag! You should never have any content other than SetValue in a dependency property setter, as certain aspects of xaml call the SetValue directly instead of going through the property. Always use the coerce property callback (if you want to validate the data before it's set) or the property changed callback (if you want to act after the property is changed, as I show in the example below).
You should do this instead:
public static DependencyProperty CurrentViewProperty =
DependencyProperty.Register("CurrentView", typeof(ViewStyles), typeof(ComboView),
new FrameworkPropertyMetadata(CurrentViewPropertyChanged));
private static void CurrentViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyControl mc = (MyControl)d;
mc.UpdateView();
}
Instead of binding the view, why not create a templated control and then bind the control's view to the property on your viewmodel?
You may also have to use data template triggers on your template to get the desired functionality.
Check out this article for help on template basics and this one for a more in depth discussion.
I want to use a color picker in my wpf application and I saw a nice looking one on this codeproject page. The control works fine until I want to connect the control to a viewmodel.
I created a small test program with this viewmodel:
public class ColorViewModel : ViewModelBase
{
public ColorViewModel()
{
LineColor = Brushes.Yellow;
}
SolidColorBrush _brushColor;
public SolidColorBrush LineColor
{
get { return _brushColor; }
set
{
_brushColor = value;
RaisePropertyChanged(() => LineColor);
}
}
}
The test program has a textbox and the colorpicker controls:
<StackPanel Orientation="Horizontal">
<TextBlock Text="Please Select a Color" FontWeight="Bold" Margin="10"
Foreground="{Binding Path=LineColor, UpdateSourceTrigger=PropertyChanged}"/>
<vw:ColorPickerControlView x:Name="ForeColorPicker" Margin="10"
CurrentColor="{Binding Path=LineColor, UpdateSourceTrigger=PropertyChanged }"/>
</StackPanel>
In the loaded event of the main window in my test application I set the viewmodel to the datacontext like this:
DataContext = new ColorViewModel();
The problem is that I can't seem to bind the LineColor property of the viewmodel to the CurrentColor property of the ColorPickerControlView. The CurrentControl property of the ColorPickerControlView seems to be fine. The constructor looks like this:
public ColorPickerControlView()
{
this.DataContext = this;
InitializeComponent();
CommandBindings.Add(new CommandBinding(SelectColorCommand, SelectColorCommandExecute));
}
In the constructor of the UserControl there is the line this.DataContext = this; I read that is is necessary to bind the dependency properties. Do I override this line when I set my viewmodel to the datacontext and is that why I can't bind to the CurrentColor property? Is there any workaround? Or did I make another mistake?
You are right in thinking that the DataContext=this phrase in the UserControl's constructor preempts if from binding to an external viewmodel. It was disccussed in this question. This is easily remedied however. There is only one DependencyProperty in the UserControl's code behind that the xaml binds to: CurrentColor.
Do this:
Add a Name="Root" attribute to the UserControl tag of the
UserControl's xaml
Change the attribute (of the Border tag)
Background="{Binding
Path=CurrentColor}" to:
Background="{Binding
ElementName=Root,
Path=CurrentColor}"
Remove the offending DataContext=this
line from the UserControl's
constructor!
That should be all that there is to it. I wrote a proof of concept that demonstrates the above. If you like I can post it, but the code above should be all you need.
Both binding must be clashing the set the value of the property. Try Setting the Mode=OneWay
<StackPanel Orientation="Horizontal">
<TextBlock Text="Please Select a Color" FontWeight="Bold" Margin="10"
Foreground="{Binding Path=LineColor, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
<vw:ColorPickerControlView x:Name="ForeColorPicker" Margin="10"
CurrentColor="{Binding Path=LineColor, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay }"/>
</StackPanel>
The line this.DataContext = this isn't really needed since you are replacing the DataContext with an instance of the ViewModel. You also do not need to assign the DataContext on the Loaded event handler. Just set it on the constructor. You can set it after the call to InitializeComponent method.
Remove the line DataContext = this in file ColorPickerControlView.xaml.cs
Change the Binding in ColorPickerControlView.xaml to Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type CustomWPFColorPicker:ColorPickerControlView}},
Path=CurrentColor}"