How to using from ContextMenu in a gridCell? - c#

I have a CustomControl like this:
<Button Name="b" Height="20" Click="b_Click" Content="operation" Width="60">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy"></MenuItem>
<MenuItem Header="cut"></MenuItem>
<MenuItem Header="delete"></MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
in CodeBehind ia have :
if (b.ContextMenu != null && b.ContextMenu.IsOpen == false)
{
b.ContextMenu.PlacementTarget = b;
b.ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
ContextMenuService.SetPlacement(b, System.Windows.Controls.Primitives.PlacementMode.Bottom);
b.ContextMenu.IsOpen = true;//I have error in this Line
}
I use from my conrol in a DataGrid Cell ...when i click on my button i get an error in this line
b.ContextMenu.IsOpen = true;
"ContextMenu" cannot have logical or visual parent
how can i resolve this erorr

A sample of working app, hope this gives you required clue...
<DataGrid Margin="0,0,195,72" x:Name="A">
<DataGrid.Resources>
<ContextMenu x:Key="ContextMenu">
<MenuItem Header="Copy" Click="MenuItem_Click"></MenuItem>
<MenuItem Header="cut" Click="MenuItem_Click"></MenuItem>
<MenuItem Header="delete" Click="MenuItem_Click"></MenuItem>
</ContextMenu>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}">
</Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="A" Binding="{Binding}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>

Related

I am using a WPF SQL database datagrid and I want to edit rows and make them bold

I'm using Data Grid that is being generated by a SQL database and I want make a single row bold or italics but whenever I use this method the entire datagrid becomes bold. Can someone help me specify the row I want?
private void bold_Checked(object sender, RoutedEventArgs e)
{
gl.FontWeight = FontWeights.Bold;
}
I'm using <menucontext> to do this:
<MenuItem Name="bold" Header="_Bold" IsCheckable="True"
Checked="bold_Checked" Unchecked="bold_Unchecked">
</MenuItem>
I want to also use:
<EventSetter Event="MouseLeftRightUp"
Handler="DataGrid_MouseRightButtonUp"/>
so that I can select my row and open the menu at the same time using the right mouse button
This is the code for that:
private void DataGrid_MouseRightButtonUp(object sender, RoutedEventArgs e)
{
try
{
string Rowid = "";
DataRowView dataRowView = (DataRowView)gl.SelectedItem;
Rowid = "" + dataRowView["EMPID"];
txtID.Text = "" + dataRowView["EMPID"];
txtName.Text = "" + dataRowView["EMPName"];
combobox2.Text = "" + dataRowView["EMPRole"];
txtAddress.Text = "" + dataRowView["EMPAddress"];
txtEmail.Text = "" + dataRowView["EMPEmail"];
txtNumber.Text = "" + dataRowView["EMPNumber"];
}
catch (Exception)
{
}
}
Here's my entire Datagrid XAML code for more context:
<DataGrid x:Name="gl" Grid.Row="3" Grid.Column="1"
Grid.ColumnSpan="3" Grid.RowSpan="1"
ItemsSource="{Binding Path = tblEmp1, Mode=TwoWay}"
MinHeight="100" IsReadOnly="True"
AlternatingRowBackground="Gray" FontSize="14"
AutoGenerateColumns="False">
<DataGrid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Aqua" Offset="0.0"/>
<GradientStop Color="Aqua" Offset="1"/>
</LinearGradientBrush>
</DataGrid.Background>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<EventSetter Event="MouseLeftButtonUp" Handler="DataGrid_MouseLeftButtonUp"/>
<!--<EventSetter Event="MouseRightButtonUp" Handler="DataGridRow_MouseRightButtonUp"-->
</Style>
</DataGrid.RowStyle>
<DataGrid.ContextMenu>
<ContextMenu BorderBrush="Black" BorderThickness="1">
<MenuItem Name="edit" Header="_Edit Row" Click="edit_Click">
<MenuItem Name="Format" Header="_Format Row">
<MenuItem Name="bold" Header="_Bold"
IsCheckable="True" Checked="bold_Checked" Unchecked="bold_Unchecked" />
<MenuItem Name="italic" Header="_Italic"
IsCheckable="True" Checked="italic_Checked" Unchecked="italic_Unchecked" >
</MenuItem>
<MenuItem Name="view" Header="_View Row" Click="view_Click" />
<MenuItem Name="delete" Header="_Delete
Employee" Click="delete_Click"/>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Resources>
<Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="#178B4B" />
<Setter Property="FontSize" Value="15" />
<Setter Property="Foreground" Value="#ffffff" />
<Setter Property="FontWeight" Value="DemiBold" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="EMPID " Binding="{Binding EMPID}" />
<DataGridTextColumn Header="EMPName " Binding="{Binding EMPName}" />
<DataGridTextColumn Header="EMPRole " Binding="{Binding EMPRole}" />
<DataGridTextColumn Header="EMPAddress " Binding="{Binding EMPAddress}" />
<DataGridTextColumn Header="EMPEmail " Binding="{Binding EMPEmail}" />
<DataGridTextColumn Header="EMPNumber " Binding="{Binding EMPNumber}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Border>
</Window>
Thank you in advance
In order to operate on a row, you can place your contextmenu on a row:
<DataGrid>
<DataGrid.Resources>
<ContextMenu x:Key="rowMenu">
<MenuItem Click="MenuItem_Click" Header="Mark Bold"/>
</ContextMenu>
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ContextMenu" Value="{StaticResource rowMenu}"/>
</Style>
</DataGrid.RowStyle>
<!-- ... -->
</DataGrid>
Then in your click-handler navigate from the menuitem to the contextmenu root to the underlying row and finally set the row font properties as needed
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
var root = GetMenuRoot(sender as DependencyObject);
if (root?.PlacementTarget is DataGridRow row)
row.FontWeight = FontWeights.Bold;
}
private ContextMenu GetMenuRoot(DependencyObject current)
{
while (current != null)
{
if (current is ContextMenu cm)
return cm;
current = LogicalTreeHelper.GetParent(current);
}
return null;
}

commandparameter in Datagrid & menucontext gives null

I stuck couple of hours in trying to get parameter from ContextMenu in Datagrid using MVVM.
The parameter comes from the CommandParameter is always null, out of set {Binding} but it's not what I want.
I new in WPF, so for me it is was not helpful to read answers from here and from others questions. It always remain null.
My code is below:
<DataGrid Grid.Row="2" Margin="25,0,0,4" SelectionMode="Single" AlternationCount="2" Name="dgAltPart" AutoGenerateColumns="False" ItemsSource="{Binding Path=AltPartResult}" HorizontalAlignment="Left" VerticalAlignment="Top"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="Delete" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.manufacturer}"
Command="{Binding DeleteManufacturerCommand}"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn Header="Manufacturer" Width="175" Binding="{Binding manufacturer}"></DataGridTextColumn>
<DataGridTextColumn Header="Manufacturer Part Number" Width="200" Binding="{Binding manufacturer_pn}"></DataGridTextColumn>
<DataGridTextColumn Header="Price" Width="100" Binding="{Binding price}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
And My ViewModel:
private bool canExecute = true;
public ICommand DeleteManufacturerCommand
{
get
{
if (_deleteManufacturerCommand == null)
{
_deleteManufacturerCommand = new RelayCommand(DeleteManufacturer, param => this.canExecute);
}
return _deleteManufacturerCommand;
}
}
public void DeleteManufacturer(object obj)
{
}
In DeleteManufacturer() I always get the obj with null.
so far tried with all kinds of relative source.
What do I miss?
If you define set ContextMenu property at row level, you could bind directly to the DataContext of each row. Try this:
<DataGrid Grid.Row="2" Margin="25,0,0,4" SelectionMode="Single" AlternationCount="2"
Name="dgAltPart" AutoGenerateColumns="False"
ItemsSource="{Binding Path=AltPartResult}" HorizontalAlignment="Left" VerticalAlignment="Top"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.ItemContainerStyle>
<Style TargetType="DataGridRow">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Delete"
CommandParameter="{Binding}"
Command="{Binding PlacementTarget.Tag.DataContext.DeleteManufacturerCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ItemContainerStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Manufacturer" Width="175" Binding="{Binding manufacturer}"></DataGridTextColumn>
<DataGridTextColumn Header="Manufacturer Part Number" Width="200" Binding="{Binding manufacturer_pn}"></DataGridTextColumn>
<DataGridTextColumn Header="Price" Width="100" Binding="{Binding price}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid Grid.Row="2" Margin="25,0,0,4" SelectionMode="Single" AlternationCount="2" Name="dgAltPart" AutoGenerateColumns="False" ItemsSource="{Binding Path=AltPartResult}" HorizontalAlignment="Left" VerticalAlignment="Top" SelectedItem="{Binding SelectedManufacturer}"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="Delete" CommandParameter="{Binding SelectedManufacturer}"
Command="{Binding DeleteManufacturerCommand}"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn Header="Manufacturer" Width="175" Binding="{Binding manufacturer}"></DataGridTextColumn>
<DataGridTextColumn Header="Manufacturer Part Number" Width="200" Binding="{Binding manufacturer_pn}"></DataGridTextColumn>
<DataGridTextColumn Header="Price" Width="100" Binding="{Binding price}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
and your ViewModel should have a property like this:
private Manufacturer selectedManufacturer;
public Manufacturer SelectedManufacturer
{
get { return selectedManufacturer; }
set { selectedManufacturer = value; OnPropertyChanged(); }
}

WPF lazy binding context menu items source

I successfully bind MenuModel to MenuItem.ItemsSource. However, the items should be contextualized. Right now, I populate the ViewModel collection and the data is ready to be bound to items source, but that's not covering all my scenarios.
I need to re-populate the ViewModel collection when the context menu is opening, that is, my goal is to only populate the collection at context menu opening, because earlier don't make sense (the items should be contextualized by ListBox SelectedItem).
XAML
<Style x:Key="ActionMenuItemStyle" TargetType="MenuItem" BasedOn="{StaticResource MetroMenuItem}">
<Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
<Setter Property="Icon" Value="{StaticResource ActionMenuItemIcon}" />
<Setter Property="IsCheckable" Value="{Binding IsCheckable, Mode=OneWay}" />
<Setter Property="IsChecked" Value="{Binding IsChecked, Mode=OneWay}" />
<Setter Property="Command" Value="{Binding Action}" />
<Setter Property="CommandParameter" Value="{Binding ActionParameter}" />
</Style>
<HierarchicalDataTemplate DataType="{x:Type model:MenuModel}" ItemsSource="{Binding Children}">
<MenuItem Header="{Binding Path=Header}" Style="{StaticResource ActionMenuItemStyle}"
UsesItemContainerTemplate="True" ItemContainerTemplateSelector="{StaticResource ActionMenuItemContainerTemplateSelector}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type model:SeparatorMenuModel}">
<Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}"/>
</DataTemplate>
<ContextMenu x:Key="OneItem">
<MenuItem Header="{x:Static resx:StudioResources.Advanced}">
<MenuItem Header="{x:Static resx:StudioResources.MoveToRoutineMenu}"
ItemsSource="{Binding PlacementTarget.Tag.Routines, RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}}"
UsesItemContainerTemplate="True"
ItemContainerTemplateSelector="{StaticResource ActionMenuItemContainerTemplateSelector}"/>
</MenuItem>
</ContextMenu>
ViewModel
public IObservableCollection<IMenuModel> Routines { get; private set; }
protected override void OnViewLoaded(object view)
{
base.OnViewLoaded(view);
GetParent().Routines.CollectionChanged += (s, e) =>
{
Routines.Clear();
var names = GetParent().Routines.Where(r => r != GetParent().ActiveRoutine).Take(5).OrderBy(r => r);
var menus = names.Select(name => new MenuModel(name, new MenuAction<string>(MoveToRoutine), name)).ToList();
menus.ForEach(m => m.WithEditor(GetParent()));
Routines.AddRange(menus);
};
}
I listen to an event to re-populate the collection, but actually I need to listen to several other events, and I wonder if there is a clean way, like populate on context menu opening.

Wpf ScrollIntoView not working first time

I have a ListView include Expander, and I assign a SelectedIndex, then call ScrollIntoView to SelectedItem position. (It can auto expand.)
lv_SelectionChanged was invoke every times.
private void lv_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (Expander exp in FindVisualChildren<Expander>(lv.lv))
{
var a = (exp.Header as StackPanel).Children[0] as TextBlock;
if (a.Text.Equals((lv.lv.SelectedItem as User).group))
exp.IsExpanded = true;
}
lv.ScrollIntoView(lv.SelectedItem);
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
But, it was not working at first time. (If it's a simple ListView, it work!)
I have no idea.
Thx.
xaml
<UserControl.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<Style x:Key="GroupHeaderStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!--<Expander IsExpanded="{Binding Mode=TwoWay, Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}, Mode=FindAncestor}}" MouseRightButtonDown="Expander_MouseRightButtonDown">-->
<Expander IsExpanded="False" MouseRightButtonDown="Expander_MouseRightButtonDown">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Name="mi_ExpandAll" Header="Hide Age column" Click="mi_ExpandAll_Click"/>
<MenuItem Name="mi_CollapseAll" Header="None"/>
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="16" VerticalAlignment="Bottom"/>
<TextBlock Text="{Binding ItemCount}" FontSize="22" Foreground="Green" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" FontSize="22" Foreground="Silver" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter>
<ItemsPresenter.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Item" Click="MenuItem_Click"/>
<MenuItem Header="Menu Item 2">
<MenuItem Header="Remove Item" Click="MenuItem_Click_1"></MenuItem>
<MenuItem Header="Select last Item" Click="MenuItem_Click_2"></MenuItem>
</MenuItem>
</ContextMenu>
</ItemsPresenter.ContextMenu>
</ItemsPresenter>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ListView Margin="10" Name="lv" Grid.Row="0" ItemsSource="{Binding GroupView}" SelectedIndex="{Binding Index}" SelectedItem="{Binding Item}" SelectionChanged="lv_SelectionChanged">
<ListView.View>
<GridView>
<local:GridViewColumnExt Header="Name" Width="120" DisplayMemberBinding="{Binding Name}"/>
<local:GridViewColumnExt x:Name="colAge" Header="Age" Width="50">
<local:GridViewColumnExt.CellTemplate>
<DataTemplate>
<Button Content="{Binding Age}"></Button>
</DataTemplate>
</local:GridViewColumnExt.CellTemplate>
</local:GridViewColumnExt>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}">
</GroupStyle>
</ListView.GroupStyle>
</ListView>
I set SelectedIndex in event
private void MenuItem_Click_2(object sender, RoutedEventArgs e)
{
lv.SelectedIndex = 8;
}
Edit
I try to set IsExpanded="True", then it is work. So, if IsExpanded="False" at first, can't it scrolling?
I user expander.BringIntoView(), then it's work!

Accessing objects in resource (WPF)

I have the following code and what I want is be able to access (get reference to) the MenuItem objects in my ListBox resource but I have no idea how.
<ListBox Grid.Column="1" Grid.Row="1" MouseDoubleClick="MainListBox_MouseDoubleClick" Name="mainListBox" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.Resources>
<ContextMenu x:Key="ContextMenu">
<MenuItem Click="OpenMenuItem_Click" Header="Open" Name="openMenuItem"/>
<Separator/>
<MenuItem Click="CutMenuItem_Click" Header="Cut" Name="cutMenuItem"/>
<MenuItem Click="CopyMenuItem_Click" Header="Copy" Name="copyMenuItem"/>
<Separator/>
<MenuItem Click="DeleteMenuItem_Click" Header="Delete" Name="deleteMenuItem"/>
<MenuItem Click="RenameMenuItem_Click" Header="Rename" Name="renameMenuItem"/>
</ContextMenu>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I would like to be able to do something like:
renameMenuItem.IsEnabled = false;
But first I need to get reference to the object.
Here is an example:
var ctx = (ContextMenu)mainListBox.FindResource("ContextMenu");
var renameMenuItem = (MenuItem)ctx.Items[6];
renameMenuItem.IsEnabled = false;
You can find any MenuItems and Separators in ctx.Items
UPDATE :
This is an alternative to find MenuItem by name as you asked in comment:
foreach (var item in ctx.Items)
{
if (item is MenuItem && ((MenuItem)item).Name == "renameMenuItem")
{
var renameMenuItem = (MenuItem) item;
renameMenuItem.IsEnabled = false;
}
}

Categories