WPF button binding - c#

I've got the application with ListView which I populate with images names, weights, etc. and inside one of the cells should be 2 buttons or labels to user interaction. I've searched and tried what I find on the web, but nothing helps.
<StackPanel Orientation="Horizontal">
<Grid>
<Button
Grid.Row="0"
Command="{Binding ShowImageCommand}"
Content="Show">
</Button>
</Grid>
<Grid>
<Button Content="Annotate"
CommandParameter="{Binding}"
Command="{Binding DataContext.ShowImageCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MainWindow}}}" />
</Grid>
Neither option for Annotate button or Show works. I've tried to do it like a label, but also no success.
<Label Content="Show" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown" >
<i:InvokeCommandAction Command="{Binding ShowImageCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Label>
Here is how it looks like in class:
public MainViewModel()
{
ShowImageCommand = new RelayCommand(ShowImageMethod);
}
public ICommand ShowImageCommand { get; set; }
public void ShowImageMethod()
{
RaisePropertyChanged("ShowImageCommand");
Messenger.Default.Send<NotificationMessage>(new NotificationMessage("It's fine"));
}
I'm new to C# and MVVM pattern, so mostly I just copy-paste code from Stack and trying to make it work. Other buttons on the grid, for example, to add images to ListView are implemented in the same way, but everything works for those. Please give me some hints.

Is your viewmodel bind to your view?
DataContext = new MainViewModel();
or
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>

Like Isaudon said I haven't had
DataContext = new MainViewModel();
After I added this to MainWindow.xaml.cs is implementation works out:
<Button Content="Annotate"
CommandParameter="{Binding}"
Command="{Binding DataContext.ShowImageCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MainWindow}}}" />

Related

UWP - Command from DataGrid is not called

UWP Application (Important because there is no AncestorType)
I can't bind command (neither other values) of the ViewModel from a DataGridTemplateColumn.
Here is my current code (i have tried, literally everything)
<controls:DataGrid
x:Name="DataGrid"
Grid.Row="2"
Height="Auto"
Margin="12"
AutoGenerateColumns="False"
HorizontalContentAlignment="Center"
ItemsSource="{Binding ProviderOrders}">
<controls:DataGrid.Columns>
<controls:DataGridTemplateColumn Header="Actions" Width="*">
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Modifier" Command="{Binding DataContext.EditOrderCommand, RelativeSource={RelativeSource Mode=Self}}" CommandParameter="{Binding}" Style="{StaticResource PrimaryButton}"/>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
</controls:DataGrid.Columns>
</controls:DataGrid>
I have also tried
<Button Content="Modifier" Command="{Binding ElementName=DataGrid, Path=DataContext.EditOrderCommand}" CommandParameter="{Binding}" Style="{StaticResource PrimaryButton}"/>
There is no error but my Command is not runned and my command is working if i move the Button outside the DataGrid..
The DataGridTemplateColumn DataContext is the ProviderOrder object and so i need to access of the ViewModel (which is obviously not accessible from the ProviderOrder object)
Thanks in advance :)
Great question, this known issue in DataGrid control. Currently, there is a workaroung for this scenario that bind command for button in CellTemplate, please add the command in the datasouce.
public class Item
{
public string ID { get; set; }
public ICommand BtnCommand
{
get
{
return new CommadEventHandler<Item>((s) => BtnClick(s));
}
}
private void BtnClick(Item s)
{
}
}
Xaml Code
<controls:DataGridTemplateColumn>
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding BtnCommand}"
Content="Click"
/>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
Update
If not specific DataGrid, you could use listview to replace, and Binding ElementName will work.

A command from a context menu that does not work

Well, I have a WPF project and I'm using Visual Studio 2010. I'm using C# and XAML, and I'm using the MVVM pattern.
The problem I have must be simple but I just can't see why it's not working.
Alright, so I have a project with a ListBox. In that ListBox are many ChatNodes; each represented visually. The visual element for a ChatNode is here:
<ControlTemplate x:Key="NodeVisualTemplate">
<Grid>
<Border BorderThickness="2" Margin="2" CornerRadius="5,5,5,5" BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.SelectionMode, Converter={StaticResource SelectionModeToColourConverter}}" ContextMenu="{StaticResource ChatNodeMenu}">
<StackPanel Opacity="{Binding IsInvisibleNode, Converter={StaticResource ResourceKey=VisibleToOpacityConverter}}">
<TextBlock Text="Test" Background="AntiqueWhite"/>
<TextBlock Text="{Binding Path=NodeText}" Background="Aqua"/>
<StackPanel Orientation="Horizontal">
<TextBox Text="Type here" MinWidth="50"/>
<Image Source="{StaticResource ImgFolder}" Margin="0,0,5,0" Width="32" Height="32"/>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
I draw your attention to the BorderBrush for the Border. I will show that again here:
BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.SelectionMode, Converter={StaticResource SelectionModeToColourConverter}}"
This code sits inside a ListBoxItem and is able to find the parent ListBox and then access a property of it. This works fine. The property is in a view model called ChatNodeListViewModel and looks like this:
private int _selectionMode = 0;
public int SelectionMode
{
get { return _selectionMode; }
set
{
if (_selectionMode != value)
{
_selectionMode = value;
RaisePropertyChanged("SelectionMode");
}
}
}
I mention it specifically because another thing which is almost identical is not working, even though this BorderBrush code is working.
So, on to the not working part.
In that same ControlTemplate above, we see a ContextMenu, called 'ChatNodeMenu'. This is as follows:
<ContextMenu x:Key="ChatNodeMenu" >
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.RemoveChatNodeCommand}" />
</ContextMenu>
It has the same binding, only this time it is for a Command called 'RemoveChatNodeCommand'. The menu does appear on a right click on a ChatNode, but the command does not run. I have actually used almost identical code in other parts of my project so I assume it's correct... but clearly there is an error somewhere.
So where is this command? It is in the view model called 'ChatNodeListViewModel' and I will present it here:
void RemoveChatNodeExecute()
{
MessageBox.Show("Remove chat node");
return;
}
bool CanRemoveChatNode()
{
return true;
}
public ICommand RemoveChatNodeCommand { get { return new RelayCommand(RemoveChatNodeExecute, CanRemoveChatNode); } }
I have also used this in many places throughout my code and it's worked every time but this one.
So, either there's a fault in the code or perhaps a simple mistake. I've checked the name of the command and re-copied it several times. I've checked the other parts of my code where I've used the same code, but I can't see anything wrong. I've cleaned and rebuilt my project just in case.
If anyone can venture a guess, I would be very happy with that.
The ListBox is not a visual ancestor of a ContextMenu but you could set the Tag property of the Border to the ListBox and then bind to the PlacementTarget of the ContextMenu:
<Border ... Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}" ContextMenu="{StaticResource ChatNodeMenu}">
<MenuItem Header="Remove" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag.DataContext.RemoveChatNodeCommand}" />
I have same issue related to conextmenu not able to work on command. could you help me to find solution.
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="test2">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="rr2" Command="{Binding DataContext.NextCommand, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" CommandParameter="{Binding}" ></MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

Using same x:Name in usercontrols with MVVM Light

I use MVVM Light in my WPF-application.
I have some usercontrol. It passes element by elementname as commandparameter to command.
<UserControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding LoadCommand}" CommandParameter="{Binding ElementName=Image}"></cmd:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
<image Name="Image" />
</UserControl>
In my MainView I use this usercontrols:
<Window>
<l:mycontrol />
<l:mycontrol />
</Window>
ViewModel
class MainViewModel:ViewModelBase
{
public MainViewModel()
{
LoadCommand = new RelayCommand<Image>(OnLoad);
}
private void OnLoad(Image image)
{
Draw(image,bitmap);
}
}
"Draw" is my function for writing on Image with some usercontrol's parameters.
Cause Images have the same x:name, my application draws only on the last image.
How else I can pass my element to command?

UserControl doesn't display inside a window

After many searches here and also in tutorials i came up empty handed and would very appreciate help:
I have a UserControl that contains a list of other kind of UserControls.
I want to display this UserControl inside a regular WPF window:
This is the UserControl that contains the list of UserControls:
<UserControl x:Class="UsersInfo.InfoLinesContainer"...
...
<Grid>
<ListView ItemsSource="{Binding Path=InfoLineUC_ObservableList,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
</ListView>
</Grid>
And in my main window i do:
<Window x:Class="UsersInfo.MainAppWindow"
...
xmlns:usrCtr ="clr-namespace:UsersInfo"
...
<usrCtr:InfoLinesContainer Grid.Row="11"
DataContext="{Binding Path=TheInfoLines, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
ScrollViewer.CanContentScroll="True" Margin="2" Grid.ColumnSpan="7" Grid.RowSpan="6" />
...
TheInfoLines is the instance of the UserControl that contains the list of other UserControls
When i run the program the window comes up without the UserControl inside it(all i get is an empty square)
How can i make it work?
In your main window :
<usrCtr:InfoLinesContainer DataContext="{Binding Path=TheInfoLines, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
What are you trying to bind here ? There seems to be no ancestor of type UserControl in your window.

ComboBox in a ListBox does not fire SelectionChanged event

I have a wpf, mvvm app, using the catel (http://catel.codeplex.com) framework\toolkit, C# .Net 4.0. The app has a ListBox with a TextBlock and ComboBox. The ListBox and ComboBox are populated from 2 different ObservableCollection from the ViewModel. I need to save (to a db), when the user clicks a button, each row in the ListBox where the user has selected an item from the ComboBox. The SelectionChanged event does not fire for any of the ComboBoxes in the ListBox. The idea being that I add to a list (ArrayList or IList?), in the ViewModel, each time the user selects an item in a ComboBox and for what row the item has been selected.
Or am I going about this the wrong way by trying to use the ComboBoxe SelectionChanged event? I also tried iterating thru the ListBox.Items but this seems like a hak and I want to avoid ui element logic in the ViewModel if possible.
The xaml:
<Grid>
<StackPanel Orientation="Horizontal">
<Label Width="180">Field1</Label>
<ListBox Height="200"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding List1, Mode=OneWay}"
Name="listBox1"
SelectionMode="Single"
Width="300">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="290">
<TextBlock Width="90" Text="{Binding}"></TextBlock>
<ComboBox Width="180" ItemsSource="{Binding DataContext.List2, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" DisplayMemberPath="Field1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<catel:EventToCommand Command="{Binding SelectionChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
ViewModel code:
//in the ViewModel constructor
SelectionChangedCommand = new Command<SelectionChangedEventArgs>(OnSelectionChangedCommandExecute, OnSelectionChangedCommandCanExecute);
public Command<SelectionChangedEventArgs> SelectionChangedCommand { get; private set; }
private bool OnSelectionChangedCommandCanExecute()
{
return true;
}
private void OnSelectionChangedCommandExecute(SelectionChangedEventArgs e)
{
// add or update list....
}
In Command binding you have used binding which has relative source binding...
consider making these changes in binding
1) using list box as Ancestortype
2) While binding use Path=DataContext.SelectionChangedCommand otherwise it will take list box as datacontext.
<catel:EventToCommand Command="{Binding Path=DataContext.SelectionChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />

Categories