Button Command Binding - c#

I have a button inside a listbox.
I want to bind the command to the DataContext of the Main Grid.
I'm not sure who to do this, below is my attempt.
I want to bind to ViewModel.SelectionEditorSelectionSelectedCommand on my view model, which the main grid is bound to, I don't want to bind to the actual filteredSelection.SelectionEditorSelectionSelectedCommand
Here is my XAML
<Grid Name="MainGrid">
.....
<ListBox x:Name="MarketsListBox" Height="Auto" MaxHeight="80" ItemsSource="{Binding Path=FilteredMarkets}" Margin="5" Width="Auto" HorizontalAlignment="Stretch"
>
ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Horizontal">
<Button Height="Auto"
Content="{Binding FinishingPosition,Converter={StaticResource FinishingPositionToShortStringConverter1}}"
Foreground="{Binding Path=FinishingPosition, Converter={StaticResource FinishingPositionToColourConverter1}}"
Margin="2" Width="20"
Command="{Binding ElementName=MainGrid.DataContext, Path=SelectionEditorSelectionSelectedCommand}"
CommandParameter="{Binding}"
/>
.....

Binding to the grid using ElementName should work, but you have made a small error in the binding syntax. ElementName must include the name only, not a property. You simply need to include DataContext in the Path:
Command="{Binding ElementName=MainGrid,
Path=DataContext.SelectionEditorSelectionSelectedCommand}"

So based on this line:
Command="{Binding ElementName=MainGrid.DataContext ... }
I'm assuming you have something like this:
<Grid Name="MainGrid">
<Grid.DataContext>
<lol:GridViewModel /> <!--Some kind of view model of sorts-->
</Grid.DataContext>
... content
</Grid>
Then all you would have to do is on the ViewModel class create a public property that returns some sort of ICommand, such as:
class GridViewModel {
public ICommand SelectionEditorSelectionSelectedCommand {
get { return new TestCommand(); }
}
}
Where TestCommand would be some kind of class implementing ICommand as in:
class TestCommand : ICommand {
public event EventHandler CanExecuteChanged { get; set; }
public bool CanExecute(object parameter)
{
return true; // Expresses whether the command is operable or disabled.
}
public void Execute(object parameter)
{
// The code to execute here when the command fires.
}
}
Basically, for ICommand you just need to define what happens when the command Executes, how to determine whether or not it CanExecute and then supply an event handle for when CanExecuteChanged. Once you have this setup, all you have to do is wire up your button like this:
<Button Command="{Binding SelectionEditorSelectionSelectedCommand}" />
And that's it. Basically the binding will automatically check your ViewModel class for a property called SelectionEditorSelectionSelectedCommand, that implements ICommand. When it reads the property it will instantiate an instance of TestCommand, and WPF will handle it from there. When the button is clicked Execute will be fired like clockwork.

You should try as I did in a similar situation:
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Grid}}, Path=DataContext.YOURCOMMANDHERE}" />
I had a button inside a TabItem Header and it worked Ok!
The thing is, your Command is a Property of the DataContext, so your path should indicate it.
Good Luck!
EDIT: Elementname might work as well.

Related

Fire a command from a Button inside a ContentControl?

I'm new to WPF and I'm trying to dynamically add a Button inside a ContentControl, which should fire a command when clicked. I'm using MVVMLight to handle the Commands.
Below I have an example with two buttons. The top button is placed directly into the StackPanel. This button fires off the Command as expected.
The second button is placed inside a ContentControl. It displays correctly, but the Command does not fire when the button is clicked.
I assumed this is because the Binding does not transfer down through the DataTemplate, but it seems to work if I use regular Commands instead of MVVMLight RelayCommands.
I don't want to remove the framework, so I'm wondering if anyone knows how to fix it? Thanks
<Window x:Class="ContentControlExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ContentControlExample.ViewModel">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="MyButton" >
<Button Content="SUBMIT" Command="{Binding MyCommand}" Width="200" Height="50"/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<!--When this button is clicked, the Command executes as expected-->
<Button Content="SUBMIT" Command="{Binding MyCommand}" Width="200" Height="50"/>
<!--Nothing happens when this button is clicked-->
<ContentControl ContentTemplate="{StaticResource MyButton}"/>
</StackPanel>
</Window>
Here's the ViewModel with the command:
public class MainViewModel : ViewModelBase
{
public ICommand MyCommand { get; private set; }
public MainViewModel()
{
MyCommand = new RelayCommand(MyCommand_Executed, MyCommand_CanExecute);
}
private bool MyCommand_CanExecute()
{
return true;
}
private void MyCommand_Executed()
{
MessageBox.Show("The command executed");
}
}
The problem here is the implicit DataContext in ContentTemplate is the Content and this has not been set to anything. You need to set Content to some Binding to bridge the DataContext currently in the visual tree, something like this:
<ContentControl ContentTemplate="{StaticResource MyButton}" Content="{Binding}"/>
Another solution is to give your Window a name:
<Window x:Class="ContentControlExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ContentControlExample.ViewModel"
x:Name="_this">
Then bind via its context instead:
<Button Content="SUBMIT" Command="{Binding ElementName=_this, Path=DataContext.MyCommand}" Width="200" Height="50"/>
This is particularly handy for things like ListViews and ItemControls, as their DCs get set to the list elements. Keep in mind though that this will only work on members within the same visual tree, if that's not the case (e.g. popup menus etc) then you need to proxy a binding as described in this article.

How to pass MenuItem as command parameter for its child control

I have something like below. For MenuItem, here I am passing an object of that MenuItem as a CommandParameter. This works fine for me. My MenuItem holds a RadioButton and I want to use the MenuItem CommandParameter value for this RadioButton. Could anyone please help me how to do this. Thanks in Advance.
<MenuItem Header="Name"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Command="{Binding SortCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}">
<MenuItem.Icon>
<RadioButton VerticalAlignment="Center"
Margin="3"
IsChecked="True"
GroupName="sort"
Command="{Binding SortCommand}"
CommandParameter="..." />
</MenuItem.Icon>
</MenuItem>
Now Command is executing only when I select the MenuItem. I want to do the same when user selects the RadioButton also. Below is the code which I am using for this.
public void OnSortCommandExecuted(object menuItem)
{
MenuItem menu = menuItem as MenuItem;
if (menu != null)
{
((RadioButton)menu.Icon).IsChecked = !((RadioButton)menu.Icon).IsChecked;
this.eAggregator.GetEvent<ImagesSortedEvent>().Publish(menu.Header.ToString());
}
}
Like I said in the comments as well, it's not a good practise to pass on UI component as CommandParameter to ViewModel since ViewModel shouldn't know about View.
I would suggest you to have proper binding in ViewModel. Create a bool property in ViewModel and bind with IsChecked DP of radioButton. That ways you don't have to pass any CommandParameter from View, simply check the status of bool property from command execute method.
Now, that why MenuItem can't be accessed from RadioButton?
RadioButton doesn't lie in same Visual tree as that of MenuItem.
So, you can't use RelativeSource to travel upto MenuItem. Also ElementName binding won't work here since this to work both elements should lie in same Visual Tree.
You might find over net to use x:Reference in such cases where two elements doesn't lie in same Visual tree but that won't work here since it will create cyclic dependency.
Last thing, you have to resort with it to use Freezable class object to hold an instance of MenuItem and use that resource in your bindings.
First of all you need to define class deriving from Freezable:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy));
}
and you can use it from XAML like this to pass MenuItem:
<MenuItem Header="Name"
x:Name="menuItem"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Command="{Binding SortCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}">
<MenuItem.Resources>
<local:BindingProxy x:Key="proxy"
Data="{Binding Source={x:Reference menuItem}}"/>
</MenuItem.Resources>
<MenuItem.Icon>
<RadioButton VerticalAlignment="Center"
Margin="3"
IsChecked="True"
GroupName="sort"
Command="{Binding SortCommand}"
CommandParameter="{Binding Data.CommandParameter,
Source={StaticResource proxy}}"/>
</MenuItem.Icon>
</MenuItem>
Ofcourse you need to declare local namespace in XAML.
PS - I would still insist to use first approach to define proper bindings in ViewModel.
UPDATE
If MenuItem is placed under ContextMenu, then RelativeSource binding won't be possible. Approach described above will work in that case.
But in case you are placing MenuItem directly as child of some control (like Menu), RelativeSource binding will work:
CommandParameter="{Binding CommandParameter,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}}"

How to implement commands on MenuItems

In trying to bind a Command to a MenuItem in my program, I've found that Commands don't work with MenuItems like they do with other controls. I've been using this post as a guide, but have had no luck so far. Basically my goal is to run a Command when the MenuItem is clicked.
This is my xaml after looking at the previously mentioned post. My Command is called CreateFiles:
<MenuItem Header="{DynamicResource save}" Command="{Binding Path=PlacementTarget.DataContext.CreateFiles, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
My Command is created in the window's ViewModel and is declared like normal, but I will post it anyway:
private ICommand _createFiles;
public MainWindowViewModel()
{
_createFiles = new Command(createFiles_Operations);
}
public ICommand CreateFiles { get { return _createFiles; } }
private void createFiles_Operations()
{
}
To test whether or not my Command is working I set a break point right at the first bracer. So far the program has not stopped at this break point when the MenuItem is clicked.
Since this method does not seem to work, what can I do to make Commands work with MenuItems?
Update: Command changed to ICommand
Update 2: ContextMenu & Button xaml:
<Button Click="Button_Click_1" Margin="5,4,0,0" Name="Button_1" Height="55" VerticalAlignment="Top" HorizontalAlignment="Left" Width="55" BorderBrush="Black">...
<ContextMenu x:Name="MainContextMenu" MouseLeave="ContextMenuMouseLeave" Background="White" BorderBrush="#FF959595" SnapsToDevicePixels="False">...
You need to set Mode of RelativeSource to FindAncestor to get ContextMenu:
<MenuItem Header="{DynamicResource save}"
Command="{Binding Path=PlacementTarget.DataContext.CreateFiles,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ContextMenu}}" />
You have set PlacementRectangle property to refer to itself i.e.ContextMenu. Don't set that property, ContextMenuService internally set PlacementRectangle to the element on which it is applied (in your case it will be Button).
Remove this PlacementRectangle="{Binding RelativeSource={RelativeSource Self}}" from ContextMenu.
It should be:
<ContextMenu x:Name="MainContextMenu"
MouseLeave="ContextMenuMouseLeave" Background="White"
BorderBrush="#FF959595" SnapsToDevicePixels="False">

Binding one control to another's DataContext

I bind my wpf window to app layer class (WindowVM.cs) using DataContext in Window.xaml.cs constructor (DataContext = WindowVM). But, one control (btnAdd) I want to bind to Window.xaml.cs property. So in Window.xaml.cs constructor I add this.btnAdd.DataContext. This is Window.xaml.cs constructor and property to which I want bind Button btnAdd:
public Window()
{
InitializeComponent();
DataContext = WindowVM;
this.btnAdd.DataContext = this;
}
public RelayCommand Add
{
get
{
return _add == null ? _add= new RelayCommand(AddPP, CanAddPP) : _add;
}
set
{
OnPropertyChanged("Add");
}
}
Xaml looks like this (class PP is WindowVM property):
<TextBox Name="txtName" Text="{Binding PP.Name, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtSurname" Text="{Binding PP.Surname, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding Add}" Content="Add" ... />
And - everything works, but console output this:
BindingExpression path error: 'Add' property not found on 'object' ''WindowVM'...
In next calls there isn't any console output error for property Add.
Now I am a little bit confused because of this error. Is this error because of first DataContext (to WindowVM), because there isn't property Add, but with line this.btnAdd.DataContext property Add is found and it's the reason that it works?
Simply set the DataContext of the Button in the XAML using a RelativeSource:
<Button Command="{Binding Add}" Content="Add" DataContext="{Binding Add, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
I had this problem and I know this is an oldish post but I think this might help someone who stumbles on this in the future.
what I did was declare the viewmodels as resources
<Page.Resources>
<local:LocationListViewModel x:Key="LocationList" />
<local:LocationNewViewModel x:Key="NewLocation" />
<code:BinaryImageConverter x:Key="imgConverter" />
</Page.Resources>
then which ever control I wanted to be associated with said viewmodel I added this to their datacontext
<TabItem x:Name="tabSettingsLocations" x:Uid="tabSettingsLocations"
Header="Locations"
DataContext="{StaticResource ResourceKey=LocationList}">....
<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd"
Header="Add New"
DataContext="{StaticResource ResourceKey=NewLocation}">....
<Image x:Name="imgSettingsLocationMapNew" x:Uid="imgSettingsLocationMapNew"
Source="{Binding Map, Converter={StaticResource imgConverter},
Mode=TwoWay}"
DataContext="{StaticResource ResourceKey=NewLocation}" />
So in my example above I have Listview bound to the list viewmodel and I create a new single location for my new entry. You will notice that by creating it as a resource I can bind the tabitem and the image (which is not a child of the tab item) to the new location viewmodel.
My command for the addnew location is in the new location viewmodel.
<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd"
Header="Add New"
DataContext="{StaticResource ResourceKey=NewLocation}">....
<Button x:Name="btnSettingsLocationSaveAdd" x:Uid="btnSettingsLocationSaveAdd" Content="Submit" Margin="0,80,10,0"
VerticalAlignment="Top" Style="{DynamicResource ButtonStyle}" HorizontalAlignment="Right" Width="75"
Command="{Binding AddCommand}" />.....
Which is the child of the tabitem I bound to the new location viewmodel.
I hope that helps.
When you set the DataContext-Property, your Window resets the Bindings of it's child controls. Even the Binding of your button.
At this Point (before "button.DataContext = this" is evaluated) "Add" is searched in WindowVM. After this you set the Window class as buttons DC, and everything works fine.
To avoid the initial error, swap two lines from this
public Window()
{
InitializeComponent();
DataContext = WindowVM;
this.btnAdd.DataContext = this;
}
to this
public Window()
{
InitializeComponent();
this.btnAdd.DataContext = this;
DataContext = WindowVM;
}

WPF Binding Via StaticResouces

Given the following Xaml:
<Window.Resources>
<System:String x:Key="StringValue"></System:String>
</Window.Resources>
<Grid>
<ComboBox Margin="137,101,169,183" ItemsSource="{Binding collection}" SnapsToDevicePixels="True" IsHitTestVisible="true">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Command="{Binding CheckCommand}" IsChecked="{Binding IsChecked}" Content="{Binding Name}"/>
<TextBlock Text="{StaticResource StringValue}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
What I want is for the Textblock Text to be bound to a static resource, that is databound to a value on the ViewModel. The issue is System.String appears to not allow databinding. ANybody know of a way to do this? For context, the TextBlock needs a different itemssource than that of its parent combobox.
Thanks.
String doesnt allow binding because it is not a DependencyObject (and doesnt implement INotifyPropertyChanged)
but why dont you just bind directly to the Value in the ViewModel?
if you cannot bind to a ViewModel (think about RelativeSource with searching Parent type) you can implement a wrapper (which implements INotifyPropertyChanged to get the changes in the object)
Example wrapper class:
public class BindWrapper<T> : INotifyPropertyChanged
{
private T _Content;
public T Content
{
get
{
return _Content;
}
set
{
_Content = value;
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
how to instantiate and bind in XAML:
<Window.Resources>
<local:BindWrapper x:Key="wrapper" x:TypeArguments="System:String">
<local:BindWrapper.Content>
<System:String>huuu</System:String>
</local:BindWrapper.Content>
</local:BindWrapper>
</Window.Resources>
<TextBlock Text="{Binding Source={StaticResource wrapper}, Path=Content}" />
To clarify, A System.String has no dependency properties so you can't bind it anything. I think you need a convertor so your TextBlock can bind to the View Model. What type of ObservableCollection do you have on the View Model?
EDIT If you just want to bind a simple string to the text property this is the wrong answer. If you want to bind to formatted text, read on.
I was having this problem before. I wanted to bind my TextBlock to a string resource in my properties. I ended up subclassing TextBlock to BindableTextBlock and making and a Convertor for string to an Inline list.
Question and Answers here.
It may seem a little involved, there ought to be an easier way. However I've resused the control several times whenever I've needed to bind to some formatted text and it works. Hopefully you can benefit from my work, and perhaps improve.

Categories

Resources