Binding with ElementName in the nested UserControls - c#

I have the following simple code:
<Window x:Class="WpfApplication3.MainWindow"
x:Name="WindowInst" …>
<local:UserControl1/>
</Window>
<UserControl x:Class="WpfApplication3.UserControl1" …>
<Button Content="Click me"
Command="{Binding DataContext.ButtonClickedCommand,
ElementName=WindowInst}" Height="134" Width="314" />
</UserControl>
And in the ViewModel for the Window I have ButtonClickedCommand:
#region Avatar click command
RelayCommand _buttonClickedCommand;
public ICommand ButtonClickedCommand
{
get
{
if (_buttonClickedCommand == null)
{
_buttonClickedCommand = new RelayCommand(() => this.ButtonClicked());
}
return _buttonClickedCommand;
}
}
public void ButtonClicked()
{
}
#endregion
Unfortunately, it causes exception at runtime:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=WindowInst'. BindingExpression:Path=DataContext.ButtonClickedCommand; DataItem=null; target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
Could you explain me what’s wrong with it?

Try modifying your binding as follows...
<Window x:Class="WpfApplication3.MainWindow"
x:Name="WindowInst" …>
<local:UserControl1/>
</Window>
<UserControl x:Class="WpfApplication3.UserControl1" …>
<Button Content="Click me"
Command="{Binding Path=ButtonClickedCommand, Mode=FindAncestor, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Height="134" Width="314" />
</UserControl>
This should work as WindowInst does not live within Self since your container is the UserControl; which is being placed within the Window. In addition you need to make sure that you are setting your DataContext within the Window or its value will be null and no binding will ever occur no matter if your syntax is accurate or not.

Your bindings are a little off.
Please see this tutorial on WPF command binding.
As a general rule, specify as little as possible in your bindings. I don't think you need element name in this circumstance and datacontext is the assumed root of your bindings.

Related

MVVM Light RelayCommand binding not working in user control

I'm working on a WPF application which uses the MVVM Light toolkit. I'm creating a wizard and I want to show buttons for navigating to the previous step and the next step on every page. In order to avoid code duplication, I use a user control which provides the buttons.
I'm trying to bind the next button's Command property to a dependency property , which is defined in the code behind file, called NextStepCommand. The type of this property is RelayCommand. The user control's dependency property NextStepCommand should then be bound to the window's property with the exact same name NextStepCommand; of course it also is of type RelayCommand.
However, the binding from the window's property NextStepCommand to the button's property Command doesn't work. Binding the window's property NextStepCommand to an arbitrary button defined in the window's XAML file works fine; so does implementing a RelayCommand in the user control's code behind class and binding it to the next button defined in the user control. Nevertheless, the full link from the window's property to the user control's button does not work and I can't figure out a solution.
The user control's XAML code is shown below.
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DockPanel>
<Button DockPanel.Dock="Left"
IsEnabled="{Binding Path=PreviousStepEnabled}"
Command="{Binding Path=PreviousStepCommand}">Back</Button>
<Button DockPanel.Dock="Right"
IsEnabled="{Binding Path=NextStepEnabled}"
Command="{Binding Path=NextStepCommand, Mode=OneWay}">Next</Button>
<Label/>
</DockPanel>
</UserControl>
The user control's NextStepCommand is defined the following way:
public RelayCommand NextStepCommand
{
get { return (RelayCommand)GetValue(NextStepCommandProperty); }
set { SetValue(NextStepCommandProperty, value); }
}
public static readonly DependencyProperty NextStepCommandProperty =
DependencyProperty.Register(nameof(NextStepCommand), typeof(RelayCommand), typeof(WizardStepSwitchBar), new PropertyMetadata(default(RelayCommand)));
The window's XAML is displayed below.
<MahApps:MetroWindow
xmlns:MahApps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
DataContext="{Binding Source={StaticResource Locator}, Path=BasicSettings}">
<Grid Style="{StaticResource MainContainerMargin}">
<control:WizardStepSwitchBar Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" PreviousStepEnabled="False" NextStepCommand="{Binding Path=NextStepCommand, Mode=OneWay}"/>
</Grid>
</MahApps:MetroWindow>
The implementation of the window's NextStepCommand property is quite simple:
public RelayCommand NextStepCommand
{
get
{
return new RelayCommand(this.OnNextStep);
}
}
private void OnNextStep()
{
MessageBox.Show("It works!");
}
I tried using this answer, but didn't provide a solution to my issue. Thank you in advance for your support!

Keybinding a TextBox using XAML and WPF in C#

I'm trying to bind to a key event in a WPF UserControl. The component is a TextBox and my XAML is
<TextBox Name="textBarCode" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="300">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding ImportPanel.BarcodeTextEnterKeyCommand}"/>
<KeyBinding Key="Tab" Command="{Binding ImportPanel.BarcodeTextTabKeyCommand}"/>
</TextBox.InputBindings>
</TextBox>
I'm not sure if it is needed or not but the namespace declaration is
<UserControl x:Class="Scimatic.Samples.Actions.ImportPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ig="http://schemas.infragistics.com/xaml"
xmlns:components="clr-namespace:Scimatic.Mondrian.Components;assembly=Mondrian"
xmlns:sampleInventory="clr-namespace:Scimatic.Mondrian.Views.SampleInventory;assembly=Mondrian"
xmlns:trackingTags="clr-namespace:Scimatic.Mondrian.Views.TrackingTags;assembly=Mondrian">
The code that declares the command in the underlying xaml.cs file is
_barcodeKeyCommand = new ActionCommand(() =>
{
if (!_parent && FocusableTagOnBarcode != null)
{
trackingInfo.SetFocusOnTag(FocusableTagOnBarcode);
}
else
{
buttonImport.Focus();
}
});
The code that sets these properties is:
/// <summary>
/// The command property for the enter key in the barcode text box
/// </summary>
public ICommand BarcodeTextTabKeyCommand
{
get
{
return _barcodeKeyCommand;
}
}
The commands are returned in the same class using the same method:
/// <summary>
/// The command property for the enter key in the barcode text box
/// </summary>
public ICommand BarcodeTextEnterKeyCommand
{
get
{
return _barcodeKeyCommand;
}
}
However no matter what I try (and I've tried all kinds of things); I just cannot get the command to be called. I've clearly done something wring but could someone please help me. I'm fairly new to C# and I've wasted two days trying to respond to an enter key in a text box!
Thank you in advance,
Regards,
Neil
Binding will look for a specified binding Path in current binding context. By default it will be current DataContext. You can change binding context by using either ElementName, Source or RelativeSource. So in your case, assuming that BarcodeTextEnterKeyCommand is a property of ImportPanel control, you can give your control some name and then change command binding
<UserControl
x:Class="Scimatic.Samples.Actions.ImportPanel"
...
x:Name="myUserControl">
<!-- ... -->
<TextBox Name="textBarCode" ....>
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding ElementName=myUserControl, Path=BarcodeTextEnterKeyCommand}"/>
<KeyBinding Key="Tab" Command="{Binding ElementName=myUserControl, Path=BarcodeTextEnterKeyCommand}"/>
</TextBox.InputBindings>
</TextBox>
<!-- ... -->
</UserControl>

Binding Dependency Property to Current DataContext Property

I keep trying to make this hurdle in WPF, and I think I've found a solution, albeit an ugly one.
The scenario is as follows:
I have a custom user control with a custom dependency property.
The user controls can be nested inside of my other user controls.
Each of my user controls has a data context that is specified by a locator (I am following the MVVM pattern)
I want to bind the custom dependency property to a value in the parent view model.
Code...
Parent View
<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
<my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" />
</UserControl>
Parent Class View Model
public class ParentClassViewModel : BaseViewModel
{
private string _demoTextAlpha = "Some Alpha text";
public string DemoTextAlpha
{
get
{
return this._demoTextAlpha;
}
set
{
this._demoTextAlpha = value;
this.NotifyPropertyChange("DemoTextAlpha");
}
}
}
Child View
<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ChildControlLocator}">
<TextBlock Text="{Binding Path=SomeProperty}" />
</UserControl>
Child View Code Behind
public partial class Child : UserControl
{
public Child()
{
InitializeComponent();
}
public static readonly DependencyProperty DemoProperty =
DependencyProperty.Register("Demo",
typeof(string),
typeof(Child),
new FrameworkPropertyMetadata()
{
PropertyChangedCallback = OnDemoChanged,
BindsTwoWayByDefault = true
});
public string Demo
{
get { return this.GetValue(DemoProperty).ToString(); }
set { this.SetValue(DemoProperty, value); }
}
private static void OnDemoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (Child)d;
var viewModel = (ChildViewModel)control.DataContext;
viewModel.SomeProperty = (string)e.NewValue;
}
}
Child View Model
public class ChildViewModel : BaseViewModel
{
private string _someProperty;
public string SomeProperty
{
get
{
return _someProperty;
}
set
{
_someProperty = value;
this.NotifyPropertyChange("SomeProperty");
}
}
}
Ok, so this WORKS. What I'm trying to achieve is better/ more elegant code, particularly as it regards to this statement.
<my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" />
Even that I could live with, as far as elegance goes, but one thing that is bothering me right now is that when I type
Path=DataContext.DemoTextAlpha
The intellisense drops when I try to drill down inside the DataContext. So I have to be extra careful to type everything right.
So - is there any different way to make the properties of the DataContext appear in intellisense, or is there an alternative way to achieve the same thing that I'm doing now?
Thanks.
EDIT to Clarify
When I put something like this instead of specifying the relative source as in the above examples...
<my:Child Demo="{Binding DemoTextAlpha}"/>
I receive an error...
System.Windows.Data Error: 40 : BindingExpression path error: 'DemoTextAlpha' property not found on 'object' ''ChildViewModel' (HashCode=34126977)'. BindingExpression:Path=DemoTextAlpha; DataItem='ChildViewModel' (HashCode=34126977); target element is 'Child' (Name=''); target property is 'Demo' (type 'String')
The DataContext (along with a lot of other properties such as FontSize) is "Inherited" along the visual tree. Therefore this:
<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
<my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}}" />
</UserControl>
Is exactly the same as this:
<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
<my:Child Demo="{Binding DemoTextAlpha}"/>
</UserControl>
With regards to the Intellisense support, I don't know what VS version you're using, but I'm using VS 2010 Pro with ReSharper 6.1 and it adds Intellisense support if you specify the d:DataContext value:
<UserControl x:Class="...etc."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TheViewModelNamespace"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DataContext="{d:DesignInstance local:ViewModel}">
Edit:
Ok.. let's analize what you're doing here:
1 - Binding the UserControl to the ParentVM:
ParentVM -> UserControl
2 - Using RelativeSource To Grab some property from ParentVM and place it into a Custom DP you created in the Child control
ParentVM -> UserControl -> Child Control
3 - In the OnPropertyChanged of the custom DP, setting that same value to the ChildVM
ParentVM -> UserControl -> Child Control -> ChildVM
Do you realize you're using the View (User Control, Child Control) as an intermediate to share some properties between 2 View Models? Why don't you just
ParentVM -> ChildVM
Which would be easier, cleaner and really MVVM?
Either put a reference from the ParentVM directly to the ChildVM, or use something like a Messenger pattern to have indirect communication between them.
DataContext is inherited:
<UserControl DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
<my:Child Demo="{Binding DemoTextAlpha}" />
</UserControl>
If, ina different scenario, your child control has a different DataContext specified and you still need to bind to a property of your parent control's DataContext, using ElementName is probably nicer:
<UserControl x:Name="Parent" DataContext="{Binding Source={StaticResource Locator}, Path=ParentControlLocator}">
<my:Child Demo="{Binding Path=DataContext.DemoTextAlpha, ElementName=Parent}" />
</UserControl>

Bind button in DataTemplate to command in the form's ViewModel

My problem is similar to the one described in this question:
WPF MVVM Button Control Binding in DataTemplate
Here is my XAML:
<Window x:Class="MissileSharp.Launcher.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MissileSharp Launcher" Height="350" Width="525">
<Grid>
<!-- when I put the button here (outside the list), the binding works -->
<!--<Button Content="test" Command="{Binding Path=FireCommand}" />-->
<ListBox ItemsSource="{Binding CommandSets}">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- I need the button here (inside the list), and here the binding does NOT work -->
<Button Content="{Binding}" Command="{Binding Path=FireCommand}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
It's just a ListBox, bound to an ObservableCollection<string> named CommandSets (which is in the ViewModel).
This binding works (it displays a button for each item in the collection).
Now I want to bind the button to a command (FireCommand), which is also in the ViewModel.
Here's the relevant part of the ViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
public ICommand FireCommand { get; set; }
public ObservableCollection<string> CommandSets { get; set; }
public MainWindowViewModel()
{
this.FireCommand = new RelayCommand(new Action<object>(this.FireMissile));
}
private void FireMissile(Object obj)
{
System.Windows.MessageBox.Show("fire");
}
}
The binding of this button does NOT work.
From what I've understood from the question I linked above, the binding doesn't work because:
(correct me if I'm wrong)
The button is inside the ListBox, so it only "knows" the binding of the ListBox (the ObservableCollection, in this case), but not the binding of the main window
I'm trying to bind to a command in the main ViewModel of the main window (which the button doesn't "know")
The command itself is definitely correct, because when I put the button outside the ListBox (see the XAML above for an example), the binding works and the command is executed.
Apparently, I "just" need to tell the button to bind to the main ViewModel of the form.
But I'm not able to figure out the right XAML syntax.
I tried several approaches that I found after some googling, but none of them worked for me:
<Button Content="{Binding}" Command="{Binding RelativeSource={RelativeSource Window}, Path=DataContext.FireCommand}" />
<Button Content="{Binding}" Command="{Binding Path=FireCommand, Source={StaticResource MainWindow}}" />
<Button Content="{Binding}" Command="{Binding Path=FireCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
Could someone please:
give me the proper XAML to bind the button inside the ListBox to a command in the form's MainViewModel?
point me to a link where this advanced binding stuff is explained in a way that a WPF/MVVM beginner can understand?
I'm feeling like I'm just copying and pasting arcane XAML incantations, and so far I don't have any clue (and can't find any good documentation) how I would figure out by myself in which cases I'd need RelativeSource or StaticResource or whatever instead of a "normal" binding.
It's:
{Binding DataContext.FireCommand,
RelativeSource={RelativeSource AncestorType=ListBox}}
No need to walk up to the root unless you actually change the DataContext along the way, but as the ListBox seems to bind to a property on the main VM this should be enough.
The only thing i recommend reading is the Data Binding Overview, and the Binding class documentation (including its properties).
Also here is a short explanation on how bindings are constructed: A binding consists of a source and a Path relative to that source, by default the source is the current DataContext. Sources that can be set explicitly are: Source, ElementName & RelativeSource. Setting any of those will override the DataContext as source.
So if you use a source like RelativeSource and want to access something in the DataContext on that level the DataContext needs to appear in the Path.
This may be considered unrelated by most, but this search is only 1 of 3 results that you'll find searching for data binding commands to controls inside a data template--as it relates to Xamarin Forms. So, maybe it'll help someone now-a-days.
Like me you may wonder how to bind commands inside a BindableLayout. Credit jesulink2514 for answering this at Xamarin Forums, where it's probably overlooked by many because of all the comments. Here's his solution, but I'm including the link below:
<ContenPage x:Name="MainPage">
<ListView Grid.Row="1"
ItemsSource="{Binding Customers}"
VerticalOptions="Fill"
x:Name="ListviewCustomer">
<ListView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Property}"/>
<Button Command="{Binding BindingContext.ItemCommand, Source={x:Reference MainPage}}"
CommandParameter="{Binding .}">Click me</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
https://forums.xamarin.com/discussion/comment/217355/#Comment_217355

problem with wpf command not executing when button clicked

I have the following XAML in a WPF application. I would like to bind the button to an ICommand in a view model. For some reason, I am not able to see the command from my view.
this is in a user control.
<Grid>
<Grid.DataContext>
<Binding
x:Name="SettingsData"
Path="Data" />
</Grid.DataContext>
.
.
.
<DockPanel Grid.Column="1">
<Button x:Name="SaveButton"
DockPanel.Dock="Top"
Height="25"
HorizontalAlignment="Left"
Margin="70 0 0 0"
Command="{Binding Path=SaveData}"
>Save Changes</Button>
</DockPanel>
</Grid>
Here is my ICommand object -
public ICommand SaveData
{
get
{
if (_saveData == null)
{
_saveData = new RelayCommand(
param => this.saveData(),
param => true
);
}
return _saveData ;
}
}
Does anyone have any idea why I cannot bind to this command?
Thanks for any thoughts....
Looks like you are setting the DataContext of the Grid to the Data property of your ViewModel (or object). If the object that the Data property exposes doesn't provide the SaveData command, you'll have the problem you're describing. Remember the DataContext is inherited from the parent.
If you require that the DataContext is set in that manner, and still require the button to reference the parent DataContext, one option would be to use a RelativeSource to point to an element that has the ViewModel as the DataContext.
In WPF you also have the option of making those commands static and using the {x:Static} markup extension to reach it.
Hope that helps.
EDIT: Here's an example if your <Grid> is contained in a <UserControl>.
<Button Command="{Binding Path=DataContext.SaveData,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}}}" ... />
Also, I don't know what your full XAML looks like, but I suspect that this can be simplified greatly by removing the DataContext on the Grid and Binding Data on the ItemsControl (or whatever you're using to show the list of objects).
Looking at below error, looks like your DataContext on DockPanel is bound to some sort of List:
I see this in the output window - BindingExpression path error:
'SaveData' property not found on 'object' ''List`1'
Please override Source attribute in Binding if the DataContext is not at the top level

Categories