This project is read-only.

WPF MVVM (code framework) Button on grid

Oct 16, 2014 at 6:57 AM
I have a WPF MVVM (code framework) project. I am trying to put Button on grid,
but this does not work. I can put the button on grid but click event does not trigger ???
I would be glad if you explain ViewModel and XAML file.
best regards.
Nov 4, 2014 at 6:02 PM
I am not sure what you mean by "put Button on grid". Are you trying to put a button in a Grid object? Or are you putting it in a ListBox or DataGrid of some sort?

Markus
Nov 5, 2014 at 8:51 AM
i want to put a button the grid's row. for example , Edit button or detail button.
I can put the button on grid but click event does not trigger ???
Nov 5, 2014 at 6:34 PM
I still don't know what you mean by "grid".

Whatever it is, the button should be firing a click event if you can see the button and there is nothing overlaying it (even if something transparent is overlaying it, the click may not reach the button).

Also, how are you trying to handle the click event. If you use CODE Framework, you are probably not using a real click event, right? Are you using a command/action?


Markus
Nov 6, 2014 at 6:43 AM
I have a WPF MVVM (code framework) project. I will send you my code below. this button could not fire the click action

<DataGrid Grid.Column="0" Grid.Row="1" Name="Grid"
              AutoGenerateColumns="False"
              SelectedIndex="{Binding SelectedGridIndex}"
              SelectedItem="{Binding SelectedGridModel}"
              ItemsSource="{Binding GridItems}"
              IsReadOnly="True"
              EnableRowVirtualization="True"
              MinHeight="500"
              ColumnWidth="250"                  
              RowDetailsVisibilityMode="VisibleWhenSelected"
              HorizontalGridLinesBrush="LightBlue" VerticalGridLinesBrush="LightGray"
              FontSize="14"    
              >
<DataGrid.Columns>
<DataGridTemplateColumn Width="120">
    <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="55"/>
                                <ColumnDefinition Width="3"/>
                                <ColumnDefinition Width="55"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="25"/>
                            </Grid.RowDefinitions>
                            <Button Grid.Column="0" Grid.Row="0" Content="{Binding ResourceLibrary.Resource.Delete ,FallbackValue={x:Static localize:Strings.Delete}}"
                            Command="{Binding DeleteGridRow}" 
                            Width="55" 
                            HorizontalAlignment="Center" 
                            FontWeight="Bold"
                            Background="Red" Foreground="White"/>
                            <Button Grid.Column="2" Grid.Row="0" Content="{Binding ResourceLibrary.Resource.Edit ,FallbackValue={x:Static localize:Strings.Edit}}"
                            Command="{Binding EditGridRow}" 
                            Width="55" 
                            HorizontalAlignment="Center" 
                            FontWeight="Bold"
                            />
                        </Grid>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
</DataGrid.Columns>
    </DataGrid>
Nov 6, 2014 at 6:56 AM
I am not overly familiar with the DataGrid control to be honest (I always use Lists instead). But as far as I can tell from the code you are posting here, it looks to me like the command binding may not work. If I had to guess, I would say that the DeleteGridRow and EditGridRow commands aren't on the right data context (the way they are set up here, they have to be on each grid item. Do you see any binding errors in the Visual Studio output window?

Markus
Nov 6, 2014 at 7:12 AM
I do not get any error. Button looks perfect but only click action can not start.
If i remove which button on the grid, then i put same button out of the DataGrid then it works.
Nov 6, 2014 at 7:25 AM
Then you must have a binding problem. I am sure it must show up as a message in your Output window.

If the button works on the main form, which has the ViewModel as it's data context, then the commands (like DeleteGridRow) are members of the view model itself. If you then use the same binding within the grid's data template, then the context of the DataGrid is ViewModel.GridItems, and the context of each data template for each row in the grid is ViewModel.GridItems[0....x]. So If within the template you are trying to bind to a command called DeleteGridRow, then it has to be a member of the grid item, not the view model.

And for that, there should absolutely be a message in the Output window. If it works on the main form, then you should get a binding expression error when you try it in the data grid, and if you don't get an error then, then you should get it when you move the button into the window. One of the two MUST show an error in the output window.


Markus
Nov 7, 2014 at 6:06 PM
Edited Nov 7, 2014 at 6:07 PM
iskender5 wrote:
I do not get any error. Button looks perfect but only click action can not start.
If i remove which button on the grid, then i put same button out of the DataGrid then it works.
Since it fires outside the grid, not in, then I think you'll find that you need to handle the event in the individual item bound in the grid. It's easier if I use my own code as an example. In this example, I show a list of apprentices. Each row has an open icon that, when clicked, opens the detail for the apprentice. The list is in a ApprenticesDataViewModel class, and the list, bound to a view on a collection of ApprenticesDataViewModel objects.

At first, I tried to handle the event to open the detail in ApprenticesViewModel, but found I needed to handle it in the ApprenticesDataViewModel class.

So, in code snippets,

Apprentices.xaml snippet
    <ListBox _ItemsSource="{Binding ApprenticeViewSource.View_}"
             SelectedItem="{Binding SelectedApprentice}">
        <c:ListEx.Columns>
            <c:ListColumnsCollection>
                <c:ListColumn Header="Open" IsResizable="False" Width="50">
                    <c:ListColumn.HeaderTemplate>
                        <ControlTemplate>
                            <Grid c:GridEx.RowHeights="Auto,*" HorizontalAlignment="Stretch" c:GridEx.ColumnWidths="*">
                                <Grid.RowDefinitions>
                                    <RowDefinition />
                                    <RowDefinition />
                                </Grid.RowDefinitions>
                                <TextBox Text="" IsEnabled="False" Width="0" BorderThickness="0"/>
                                <Label Content="Open" Grid.Row="1"/>
                            </Grid>
                        </ControlTemplate>
                    </c:ListColumn.HeaderTemplate>
                    <c:ListColumn.ItemTemplate>
                        <DataTemplate>
                            <Border CornerRadius="50" Width="23" Height="23" BorderBrush="{DynamicResource CODE.Framework-Application-HighlightBrush2}" BorderThickness="2">
                                <c:Ex.EventCommand>
                                   <c:EventCommand Command="{Binding OpenProfileCommand}" Event="MouseUp" />
                                </c:Ex.EventCommand>
                                <Rectangle  Width="15" Height="15" Fill="{DynamicResource CODE.Framework-Icon-OpenFile}"/>
                            </Border>
                        </DataTemplate>
                    </c:ListColumn.ItemTemplate>
                </c:ListColumn>
The OpenProfileCommand event is handled in ApprenticesDataViewModel instead of the ApprenticesViewModel.

Does this help at all? I'm not very good at the concepts to say it clearly, but I hope it helps a little.
Jan 12, 2015 at 3:34 PM
Actaully the problem is Button can not resolve the its data context and doesn't know where the command event is. So for successor of the requested button should find its ancestor and access to the command through out its DataContext, as follows:
<Button Grid.Column="0" Grid.Row="0" Content="{Binding ResourceLibrary.Resource.Delete ,FallbackValue={x:Static localize:Strings.Delete}}"
                            Command="{Binding DataContext.DeleteGridRow, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}" 
                            Width="55" 
                            HorizontalAlignment="Center" 
                            FontWeight="Bold"
                            Background="Red" Foreground="White"/>
                            <Button Grid.Column="2" Grid.Row="0" Content="{Binding ResourceLibrary.Resource.Edit ,FallbackValue={x:Static localize:Strings.Edit}}"
                            Command="{Binding DataContext.EditGridRow, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}" 
                            Width="55" 
                            HorizontalAlignment="Center" 
                            FontWeight="Bold"
                            />

Emrah
Jan 12, 2015 at 3:38 PM
This just resolves to the data context of a different object. I would not do that. I would instead make sure that each row in the items source has that command (or at least a reference to it). Resolving to the ancestor is an expensive operation, and it certainly is very expensive when it is done for each item of a data source. Also, it makes the code much harder to read/write/maintain. So worse performance and worse maintainability for no real reason. Not something I would aim for :-)

Markus