WPF DataGrid Remove Column - c#

I have created a DataGrid in WPF from a DataTable with AutoGenerateColumns = true. In the VM I have a command property for adding and deleting columns which manipulates the underlying DataTable.
When I call AddColumn from the main XAML window via Command="{Binding AddColumn}" it works as expected but when I call the RemoveColumn from a resource file with a context menu it calls the command property (im able to step through the code) but does not update the Grid.
<MenuItem Header="Delete" Command="{Binding Source={StaticResource AppViewModel}, Path=DeleteColumn}" CommandParameter="{Binding}" />
I now updated both commands to only set the DataTable to null, ie Dt = null and for the AddColumn this works as expected and removed the Grid and columns but for the RemoveColumn it does nothing... I also see no errors in the output window in regard to binding and when stepping through the code the property is called. I also tried to set the column to invisible which also did not work.
UPDATE
I call the DeleteColumn from the following (simplified) code in a resources file.
<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Style.Triggers>
<DataTrigger Binding="{Binding Content, ConverterParameter=*, Converter={StaticResource AfterDashConverter}, RelativeSource={RelativeSource self}}" Value="Green">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding Source={StaticResource AppViewModel}, Path=DeleteColumn}" />
</ContextMenu>
</Grid.ContextMenu>
<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False"/>
<ItemsPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
The commands are very simple, the AddColumn works as its directly in my main view and the DeleteColumn does not as its in the above resource file. I have checked the commands fire properly.
AddColumn = new RelayCommand(_ =>
{
Dt = null;
}, true);
DeleteColumn = new RelayCommand(column =>
{
Dt = null;
}, true);

the main issue was my resource was creating a new viewmodel so it was not calling / impacting the VM instance that my view was dependent on. To fix this I needed to set the DataContext of my ContextMenu parent control (which was a grid) as follows:
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyType}}}
If you just call the following it will create a new instance of your ViewModel and use that:
<MenuItem Header="Delete" Command="{Binding Source={StaticResource AppViewModel}, Path=DeleteColumn}" />

Related

Assign command to a set of buttons in grid through style

Is it possible in silverlight to assign a same command to a set of buttons in a grid through style? or assign it at a common place instead of repeating?
I mean something like using this -
<Style TargetType="Button">
<Setter Property="Command" Value="{Binding Command1}"/>
</Style>
instead of -
<Button Command="{Binding Command1}"/>
<Button Command="{Binding Command1}"/>
<Button Command="{Binding Command1}"/>
...
<Button Command="{Binding Command1}"/>
It is possible. In a simple way you can add a Style to the Resources and use it as a StaticResource. Assuming that Command1 belongs to the ViewModel you are binding to the Window. If not, provide proper path to the Command1 to bind it properly.
<Window.Resources>
<Style x:Key="buttonCommandStyle" TargetType="Button">
<Setter Property="Command" Value="{Binding Path=DataContext.Command1}" />
</Style>
</Window.Resources>
Then use it as the Style for your Button
<Button Style="{StaticResource buttonCommandStyle}" />
What you are looking for is the ItemsControl.
<ItemsControl x:Name="MyItemsControl" ItemsSource="{Binding MyButtonItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command1}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Binding will be evaluated against each item, therefore you have several ways of providing the command instance:
you could place the same command instance as a public property in each itemInstance
you could set a source or relativeSource for the Binding to have it evaluated against the DataContext of the ItemsControl instead of against each item: {Binding Path=DataContext.Command1, ElementName=MyItemsControl}

Command & command parameter Tree in Xaml

Trying to change one button to other, I have stuck in a problem with commands handling. It is difficult to describe its source, because nearly all are the same, but when I pass the command via setter it is not working.
My previous code (it is working like a charm):
it is just a split button that passes its menu item header as parameter to executing command.
<xctk:SplitButton.DropDownContent>
<ItemsControl ItemsSource="{Binding Instance.InstanceVM.Names}" x:Name="NamesCtr">
<ItemsControl.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Command="{Binding ElementName=NamesCtr, Path=DataContext.Instance.InstanceVM.Load}" CommandParameter="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</xctk:SplitButton.DropDownContent>
And here is for my dropdown button:
<controls:DropDownButton.DropDownContextMenu>
<ContextMenu x:Name="NamesCtr" ItemsSource="{Binding Instance.InstanceVM.Names}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Template" Value="{StaticResource DropDownMenuItemTemplate}"/>
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="Command" Value="{Binding ElementName=Namestr, Path=DataContext.Instance.InstanceVM.Load}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</controls:DropDownButton.DropDownContextMenu>
This shows a button, a dropdown with correct menu items (so the source, template and header were bound correctly, but the command does not hit its function)
Found a solution here :
How do you bind a command to a MenuItem (WPF)?
As you are binding through a static singleton, you can modify the Binding like this:
<Setter Property="Command" Value="{Binding Source={x:Static <xmlnamespace>:<yourType>.Instance}, Path=InstanceVM.Load}"/
Don't forget to replace the placeholders with valid values, i.e. with an xml namespace prefix that corresponds to the appropriate CLR namespace and the type declaring the Instance property.
Please note that ElementName bindings and RelativeSource bindings are less trustworthy if placed in DataTemplates / Styles and also in case of using Popups in your visual tree.

Passing View as Command Parameter in WPF.Does it violet MVVM approach?

I have data grid and there some number of columns in it.There are number of rows. I want to show one window when user clicks on the context menu of that row.I need first columns value in viewmodel from that row for some logic.Currently I am passing placement target as command parameter i.e.gridviewrow. Following is my code
<telerik:RadGridView.RowStyle>
<Style TargetType="telerik:GridViewRow">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" ></Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="show Window" Command="{Binding PlacementTarget.Tag.DataContext.ShowChart,RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}" CommandParameter="{Binding PlacementTarget, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</telerik:RadGridView.RowStyle>
How can I pass value of first column of particular row on which user has clicked?
Does it violet MVVM approach?What is solution if it violets MVVM approach in this case?
Teoretically yes, because the only way to access the view is by INotifyPropertyChanged and IErrorDataInfo. However, it depends what do you inttend to do. If you want to change the visibilty of a UI elemnt, I violet the MVVM pattern because the other way arround it seems to complicated to me. I suggest to tell what exactly do you want to do, and maybe I will be able to help you :)
Maybe You can try to create a new ViewModel class for the grid's rows and each column is a property of the ViewModel. So you can easily bind the data context of the row to that ViewModel and the columns to its properties.
I will post another answer since you edited masively the question.
If you use a datagrid in your view, the your view (XAML code) should look like this:
<Window x:Class="CareerTrackWpfClient.Views.User_Main_Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="User_Main_Window" Background="Black">
<Window>
<Grid HorizontalAlignment="Center" Visibility="Collapsed" Name="gridbooks">
<DataGrid Height="650" HorizontalAlignment="Stretch" Name="BooksGrid" RowHeight="90" ColumnWidth="200"
ColumnHeaderHeight="40" HeadersVisibility="Column" Background="Transparent" RowBackground="DarkGray"
AlternatingRowBackground="LightBlue" BorderBrush="Gray"
BorderThickness="2" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Book #" Width="220" Binding="{Binding BookID}" >
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Book Title" Width="220" Binding="{Binding Title}" >
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
You should have a model like this
public class Book
{
public int BookID {get; set;}
public string Title {get;set;}
}
Now, either you do a ViewModel where you fill the list (like Zarzan said) - and this way you will respect the MVVM pattern. Or in the code beheind, in the constructor, bellow the InitializeComponent() method, you write something like this
List<Book> booksProvider=new List<Book>();
booksProvider.Add(new Book{BookID=1,"Book 1"}) ;
booksProvider.Add(new Book{BookID=2,"Book 2"}) ;
gridbooks.ItemsSource=booksProvider;
Is very nice using this UI component (DataGrid), from my point of view. It has the validations on place. Try to enter strings on the BookID field, and it will notify you. It is very flexible and you don't have to install anything.
Hope it helps as many developers as possible.

How do I create a "Window" menu in XAML?

I am creating an MVVM Wpf client application. I want create menu in the main View for the application that his a menu item called "Window" on it. That menu item will dynamically update itself with a submenu of menuitems who are made up of the list of active windows running in the application. I created a ViewManager whom each View registers itself with to compile a list of active windows.
I am trying to do this in XAML but getting an error when I click on "Window"
<MenuItem Header="Window">
<ItemsControl ItemsSource="{Binding ViewMgr.Views}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.OpenWindowCmd ,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</MenuItem>
How do I create a dynamically updated list of menuitems on my menu in XAML using a MVVM style of data bindings and commands?
You are adding a new ItemsControl as a single child of the menu item, instead of adding each view as one child of the menu item itself. You probably get the error because the styles TargetType doesn't match. MenuItem inherits from ItemsControl itself and exposes a property ItemsSource. Try the following:
<MenuItem ItemsSource="{Binding ViewMgr.Views}" DisplayMemberPath="Title">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.OpenWindowCmd, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>

Bind Items to MenuItem -> use Command

I have a MenuItem, which has a collection of items in it. It looks like the File -> Open Menuitem.
So:
File
Open
Open from DataBase
File 1
File 2
File 3
XAML Code:
<Menu>
<MenuItem Header="File">
<MenuItem Header="Open">
<MenuItem Header="From Database" ItemsSource="{Binding OCFragebogen}"/>
</MenuItem>
</MenuItem>
</Menu>
I want to call a Command, when a specific item has been clicked. Example: User clicks on File 1, a command should be called where the "File 1" is the Command Parameter.
ViewModel contains the Items, which I want to display in the MenuItem "collection"
private ObservableCollection<string> _OCFragebogen;
public ObservableCollection<string> OCFragebogen
{
get
{
if (_OCFragebogen == null)
_OCFragebogen = new ObservableCollection<string>();
return _OCFragebogen;
}
set
{
_OCFragebogen = value;
RaisePropertyChanged(() => OCFragebogen);
}
}
To make it clear: When the user clicks on an item (from the ItemsSource) in the MenuItem, a Command should be called where I want to do something with the clicked Item.
Edit: Where do I have to use the command to call a method (RelayCommand) in my ViewModel? I want it to be used when an Item from the ItemsSource has been clicked + I want to pass the clicked item to the method.
This should work for you
<MenuItem Header="From Database"
ItemsSource="{Binding YourItemSource}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.YourCommandName}"></Setter>
<Setter Property="CommandParameter" Value="{Binding}"></Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
Try to change its ItemContainerStyle and then bind the command from ItemsSource item,
<MenuItem Header="From Database" ItemsSource="{Binding OCFragebogen}"
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding YourCommand}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
I haven't tried if it works just a guess
edited answer
<MenuItem Header="From Database" ItemsSource="{Binding OCFragebogen}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}, Path=DataContext.YourCommand }" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=Header}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>

Categories