How to retrieve DataRow from DataRow.ContextMenu event - c#

How can a bind DataRow to the Tag property of the ContextMenu.MenuItem associated with that row?
Here is what I have so far:
<DataGrid.Resources>
<ContextMenu x:Key="RowContextMenu">
<ContextMenu.Items>
<!--This line doesnot work-->
<MenuItem Header="GoToElement" Click="Click_GoToElement" Tag="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=Row.Header}"/>
</ContextMenu.Items>
</ContextMenu>
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="ContextMenu" Value="{StaticResource RowContextMenu}" />
</Style>
</DataGrid.RowStyle>
Then , click-event looks like this. I am receiving an error: object reference not set to an instance of an object
private void Click_GoToElement(object sender, RoutedEventArgs e)
{
try
{
var row = ((System.Windows.Controls.MenuItem)sender).Tag;
MessageBox.Show(row.ToString());
}
catch (Exception ex) { MessageBox.Show(ex.Message + "\n" + ex.StackTrace); }
}

Bind to the PlacementTarget of the parent ContextMenu:
<MenuItem Header="GoToElement"
Click="Click_GoToElement"
Tag="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.Header}"/>

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;
}

WPF binding visibility to gridview listview button

How do I bind the visibility of a button inside a listview without using converter, but using the data trigger property. But i not manage to understand how to code behind. Do we still need to use Boolean property to hold the true/ false value? If yes, how to implement that? Sorry that i am new to WPF, and still not able to understand those complex code badly.
private void Btnfolder_Click(object sender, RoutedEventArgs e)
{
try
{
// the excel file open from here to display gridview with button true and false for certain rows but i do not know how to set the visibility here
}
catch (Exception ex){
MessageBox.Show(ex.ToString());
}
}
public bool _isVisible = false;
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
OnPropertyChanged("IsVisible"); --> error- cannot convert from string to System.Windows.DependencyPropertyChangedEventArgs
}
}
<GridViewColumn Header="View CriticalPart" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnCritical" Content="Edit" Click="Button_Click" Width="80" Height="30" Margin="5" >
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

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 Listview with textbox error

I got a listview with a gridview cell template, as a textbox.
I can change the itemssource in the ui at runtime, but I can't do it, in code behind..
Please any ideas why would be a appreciated.
it's bound to a custom list.
this is my code so far:
Xaml template.
<Style TargetType="{x:Type TextBlock}" x:Key="GridBlockStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}, Converter={StaticResource boolToVis}, ConverterParameter=False}" />
</Style>
<Style TargetType="{x:Type FrameworkElement}" x:Key="GridEditStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}, Converter={StaticResource boolToVis}, ConverterParameter=True}" />
</Style>
<DataTemplate x:Key="txt_Field" DataType="{x:Type GridViewColumn}">
<Grid>
<TextBlock Text="{Binding Path=txt}" Style="{StaticResource GridBlockStyle}"/>
<TextBox Style="{StaticResource GridEditStyle}" Text="{Binding Path=txt}"/>
</Grid>
</DataTemplate>
Code behind:
DataTemplate t_txt = (DataTemplate)window.FindResource("txt_Field");
gridView.Columns.Add(new GridViewColumn { Header = "txt", CellTemplate = t_txt });
it updates the itemssource and as soon as the foreach loops are done the Entries are being overriden to null.
try
{
foreach (Items1 item in TempList)
{
foreach (Items1 item2 in list)
{
if (item.num == item2.num)
{
item2.txt = item.txt;
}
}
}
listview.Items.Refresh();
}
catch { }
I found a solution, but I still want to know why it happent..
this is the solution:
for (int i = 0; i < TempList.Count; i++)
{
if (Order_TempList[i].txt == ((Items1)listview.SelectedItem).txt)
{
((Items1)listview.SelectedItem).txt = Order_TempList[i].txt;
i = TempList.Count;
}
}
I can see the error very clearly now.
It's because there is two lists and the visual list (listview.Items) is being updated and not the physical list (TempList).

Get Text of a ContextMenu.ItemsSource generated MenuItem in WPF

I´ve got a Click-method of a MenuItem in a ContextMenu. In this method, I need the text of the item I´ve clicked.
Here´s the code:
private void menuItemKostenstellen_Click(object sender, RoutedEventArgs e) { }
I already tried with e.Source but that didn´t work.
How can I get this?
Use following:
<ContextMenu Name="conKostenstelle" >
<MenuItem Header="Kostenstellen" Name="menuItemKostenstellen">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<EventSetter Event="Click" Handler="MenuItem_Click" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
LinkedList<String> kliste = kosrep.GetKostenstellen();
menuItemKostenstellenunter.ItemsSource = kliste;
try this
private void menuItemKostenstellen_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = sender as MenuItem;
string title = mi.Header.ToString();
}
Use ItemContainerStyle Property for Click Event on all MenuItems
<ContextMenu>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<EventSetter Event="Click" Handler="MenuItem_Click" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>

Categories