"Transfer" a binding from a property to an other - c#

I am trying to wrap a TextBox in a custom UserControl and by then my custom UserControl has a Text dependency property. An example use of the custom UserControl would look like this :
<MyTextBoxWrapper Text="{Binding SomeProperty}"/>
and MyTextBoxWrapper looks like this :
<UserControl ...>
<TextBox x:Name="wrappedTextBox"/>
</UserControl>
I'd like my wrapper to work exactly like if it was written like this :
<UserControl ...>
<TextBox x:Name="wrappedTextBox" Text="{Binding SomeProperty}"/>
</UserControl>
My issue is that I can't manage to "transfer" the binding from the MyTextBoxWrapper.Text property to the TextBox.Text property. (kind of like binding the bindings themselves)
I only managed to obtain the binding used by MyTextBoxWrapper.Text with this :
myTextBoxWrapperInstance.GetBindingExpression(MyTextBoxWrapper.TextProperty);
How can I have the same binding being used for both properties? I don't necessarily need the same binding instance but I at least need the binding used by TextBox.Text to be created from the binding used by MyTextBoxWrapper.Text.

Bind the TextBox in the UserControl to the Text property of the UserControl itself:
<TextBox x:Name="wrappedTextBox"
Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
This doesn't fit my needs, I need to have the binding on TextBox.Text to be the same binding as the one on MyTextBoxWrapper.Text, what you gave me creates a different binding
Yes, a single binding always has a single target and single source.
You may try to register a callback for the custom Text property of the UserControl and programmatically bind the Text property of the TextBox to the same property.
Or make Text a property of type Binding.

Related

How to control when TextBox Text as binding source updates the target value?

Trying for a simple thing. I want TextBlock text to be updated to what TextBox's text value is. However, I want it to happen only on LostFocus. Currently below code updates a TextBlock as user is typing into a TextBox. How do we achieve that?
<StackPanel>
<TextBox x:Name="txtQty" />
<TextBlock Text="{Binding ElementName=txtQty, Path=Text}" />
</StackPanel>
I explored the UpdateSourceTrigger property on textbox with LostFocus, but it won't work as that controls how the source should be updated, whereas here I need how the destination updates.
I prefer to have a XAML only solution.
XAML is a markup language.
The straight-forward way to to this would be to bind the TextBox and the TextBlock to the same view model source property. The source property will be set when the TextBox loses focus and then the TextBlock will then be updated provided that the view model class implements the INotifyPropertyChanged interface as expected.
You could of course also handle the LostKeyboardFocus event for the TextBox and set the Text property of the TextBlock programmatically in the code-behind of the view. This approach is not any worse than trying to implement some logic in the XAML markup of the very same view. Just because you possibly can do something in pure XAML, it doesn't mean that you always should. A programming language such as C# usually does a better job implementing some logic.
As others already said, the best way would be to bind the TextBlock and the TextBox to the same viewmodel property.
If you want to do it only with XAML code you could try it from the other side and bind your TextBox to the TextBlock.
Like this:
<StackPanel>
<TextBox Text="{Binding ElementName=txtQty, Path=Text, UpdateSourceTrigger=LostFocus, Mode=OneWayToSource}" />
<TextBlock x:Name="txtQty" />
</StackPanel>

Bind property of root to value of child element in XAML

I'm not too used to WPF, so this is probably something easy, but I've been struggling with it for a couple hours and can't seem to get how to properly do it.
Say I have a BaseUserControl descending from UserControl with a dependency property Text.
Then in XAML I'm creating a BaseUserControl descendant. I want that property Text to be bound to a control defined in that descendant. Say:
<base:BaseUserControl
... all namespaces ...
xmlns:base="clr-namespace:MyControlsBase"
x:Class="Test.MyTestControl"
Text="{Binding ElementName=MyTextBox, Path=Text}"
<TextBox x:Name="MyTextBox" Text="MyText" />
</base:BaseUserControl>
For some reason, I can't get the MyTextBox to update the Text property on the control itself.
If I add a:
<TextBlock Text="{Binding ElementName=MyTextBox, Path=Text}" />
Anywhere inside the control, the textblock shows the correct TextBox value so the binding definition doesn't seem to be the problem.
I have something else which shows the value of Text in that control... say something like:
<Window>
<StackPanel>
<test:MyTestControl x:Name="MyControl" />
<TextBlock Text="{Binding ElementName=MyControl, Path=Text}" />
</StackPanel>
</Window>
If I update the Text property on MyControlBase from any other means (codebehind, or whatever), it works, and I see the text changed on the textblock... but it doesn't work seem to update when the TextBox inside itself is updated.
Are there any limitations on binding to properties when you are inheriting a control?
PS: the code is obviously artificial and boilerplated for this question
Note: there is obviously something wrong with the binding on that property, since on the trace window, when creating the control, I get a:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=MyTextBox'. BindingExpression:Path=Text; DataItem=null; target element is 'MyTestControl' (Name=''); target property is 'Text' (type 'String')
But it only happens for the `MyTestControl's property, and not for any other binding to the same property inside the XAML.
I believe the problem is that the MyTextBox hasn't been initialized when the BaseUserControl initializes itself and tries to bind with the Text property of the MyTextBox. At this stage, the MyTextBox doesn't exist, as a result you get the 'System.Windows.Data Error: 4 : Cannot find SOURCE for binding with reference'.
You can bind in code-behind after the InitializeComponent() in the CTOR of your MyTestControl.
public MyTestControl()
{
InitializeComponent();
Binding b = new Binding("Text");
b.Source = MyTextBox;
SetBinding(TextProperty, b);
}

How do I avoid binding to 2 datacontext

My application looks like the following
The black is my MainWindow, the red is a tab control and the yellow is a UserControl.
The UserControl has many Dependency Properties defined and they bind to the DataContext (Which is set in the MainWindow's code behind, using this.DataContext = this).
To bind my UserControl to the same DataContext as my MainWindow, in my UserControl xaml I have the following
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType=Window}}"
This works great, and when I interact with my UserControl, due to the two way binding, it updates the Properties of my MainWindow, which in turn updates my TabControl!
The issue is, my UserControl now has some extra functionality and as such, needs to bind to the UserControl's code behind (such as values for the GUI).
This is where I'm stuck. I can't bind from my UserControl to my code behind because I've already created a DataContext.
I know I could use the WinForms approach, and name each control with x:Name="MyControl" like
MyControl.Text = "This value";
or
MyControl.DataContext = this;
Yeuk I think!!
My question is, is this the only way to go, or can I still use binding.
First of all you don't need to manually set DataContext on UserControl. DataContext is an inheritable property so it will inherit DataContext from its parent unless you have explicitly set it.
Get rid of DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType=Window}}" from your UserControl.
And now, in case you want to bind to code behind for some controls in your UserControl, you can bind using RelativeSource or can set DataContext on control:
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl}}"
If controls can be clubbed together under one panel, set DataContext on parent panel say Grid and child controls will inherit from it:
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl}}">
..Child Controls here will inherit DataContext
</Grid>
And to answer your question:
MyControl.DataContext = this; <-- Is this possible
Yes, it is possible like i mentioned above.
You can just use another RelativeSource Binding as you have for the MainWindow properties... to access the properties defined in the UserControl, try the following XAML in your UserControl:
<TextBlock Text="{Binding UserControlProperty, RelativeSource={RelativeSource
AncestorType={x:Type YourXmlNamespacePrefix:YourUserControl}}}" />
Obviously, you'll need to update YourXmlNamespacePrefix:YourUserControl to a valid XML Namespace and control type to get this to work.
I'm not saying either that you should set the DataContext anywhere, or change any properties. This is a RelativeSource Binding... you do not need to set any DataContext to make it work. I thought you would have known that seeing as you're already using one. Just try the example out.

How can I bind a xaml property to a static variable in another class?

I have this xaml file in which I try to bind a Text-block Background to a static variable in another class, how can I achieve this ?
I know this might be silly but I just moved from Win-forms and feeling a little bit lost.
here is what I mean:
<TextBlock Text="some text"
TextWrapping="WrapWithOverflow"
Background="{Binding Path=SomeVariable}" />
First of all you can't bind to variable. You can bind only to properties from XAML.
For binding to static property you can do in this way (say you want to bind Text property of TextBlock) -
<TextBlock Text="{Binding Source={x:Static local:YourClassName.PropertyName}}"/>
where local is namespace where your class resides which you need to declare above in xaml file like this -
xmlns:local="clr-namespace:YourNameSpace"
You can't actually bind to a static property (INotifyPropertyChanged makes sense on instances only), so this should be enough...
{x:Static my:MyTestStaticClass.MyProperty}
or e.g.
<TextBox Text="{x:Static my:MyTestStaticClass.MyProperty}" Width="500" Height="100" />
make sure you include the namespace - i.e. define the my in the XAML like xmlns:my="clr-namespace:MyNamespace"
EDIT: binding from code
(There're some mixed answers on this part so I thought it made sense to expand, have it in one place)
OneTime binding:
You could just use textBlock.Text = MyStaticClass.Left (just careful where you place that, post-init)
TwoWay (or OneWayToSource) binding:
Binding binding = new Binding();
//binding.Source = typeof(MyStaticClass);
// System.InvalidOperationException: 'Binding.StaticSource cannot be set while using Binding.Source.'
binding.Path = new PropertyPath(typeof(MyStaticClass).GetProperty(nameof(MyStaticClass.Left)));
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(Window.LeftProperty, binding);
...of course if you're setting Binding from the code remove any bindings in XAML.
OneWay (property changes from the source):
And if you'd need to update the target (i.e. the control's property, Window.Left in this case) on the source property changes, that can't be achieved with the static class (as per my comment above, you'd need the INotifyPropertyChanged implemented, so you could just use a wrapper class, implement INotifyPropertyChanged and wire that to a static property of your interest (providing you know how to track you static property's changes, i.e. this is more of a 'design' issue from this point on, I'd suggest redesigning and putting it all within one 'non-static' class).
You can use the newer x:Bind to do this simply using:
<TextBlock Text="{x:Bind YourClassName.PropertyName}"/>

Own user control with new property

I want to create a control like this:
I created view and viewmodel of this control and I added new property - "FileURL".
I want to use this property like this:
<vm:FileSelector FileURL="{Binding Something}"/>
And now it is a problem:
My new control contains button and textbox.
I want that property "FileURL" of FileSelector contains the same value as "Text" of TextBox used inside FileSelector.
So in XAML declaration of new control i wanted to add something like that:
<UserControl x:Class="namespace.FileSelector"
...
FileURL="{Binding ElementName=txtBoxExampleName, Path=Text}">
But this operation is not allowed.
I do not know how can i change value of property "FileURL" inside FileSelector control using MVVM pattern.
Assuming that FileURL is a DependencyProperty in FileSelector then you would just do the following in the FileSelector.xaml:
<TextBox Text="{Binding FileURL, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"/>

Categories

Resources