I have a DataGrid created in XAML in a C# project. I've added a context menu to the rows. Basically when the user clicks directly on the cell it should open the relevant item in the current window, which is implemented on the SelectionChanged event.
However if the user right clicks a row it should show the the context menu without selecting the row, so that the user can select an item in the context menu to open the relevant item in a new window. So they can look at both the already selected item and the new item at once, but as the right click selects the row, the user see the newly selected item in the current window and the new window.
How can I stop the right click action to show the context menu from selecting the cell?
For my solution, I have to overwrite the following two event handlers (i.e., PreviewMouseRightButtonDown and PreviewMouseRightButtonUp). Plus, not sure why data-binding for the ItemsSource does not work, so that I have to bind it manually.
private void ResultDataGrid_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (sender is DataGrid dg)
{
if (this.DataContext is PipelineStepResultViewModel dataContext
&& dataContext.DatagridMenuItems != null)
{
dg.ContextMenu.ItemsSource = dataContext.DatagridMenuItems;
}
}
e.Handled = true;
}
private void ResultDataGrid_PreviewMouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (sender is DataGrid dg && dg.ContextMenu.ItemsSource != null)
{
ResultDataGrid.ContextMenu.IsOpen = true;
}
e.Handled = true;
}
Related
My project has a DataGridView with 52 columns and some of columns are not visible. But I want the user to make them visible easily. So I added a button as you see on the pic below. This makes five columns visible.
But the problem when scrolling, this Button stay static on form. I want this button anchored to a specific column header. Movable with column header.
So i tried to put the DataGridView and Button contols on same panel as children. That's worked but panel scroll is not functional as much as DataGridView's scroll. For example in panel scrolling freezed columns is not working.
Is there any solution for this. I want the user to make columns visible or not visible easily like Excel. it is not necessary to use a buttons. İf there any another options, I am interested.
I'll show an example of how you can use the context menu to show and hide DataGridView columns.
Add menu to Form:
ContextMenuStrip columnMenu;
Create menu like this. Each column has one menu item with a checkbox.
Of course, you can create and fill it in manually in the designer.
private void Form1_Load(object sender, EventArgs e)
{
columnMenu = new ContextMenuStrip();
foreach (DataGridViewColumn column in dataGridView.Columns)
{
var item = new ToolStripMenuItem();
item.Text = column.Name;
item.CheckOnClick = true;
item.Checked = true;
item.Click += Item_Click;
columnMenu.Items.Add(item);
}
}
When you select a menu item, show or hide the column.
private void Item_Click(object sender, EventArgs e)
{
var item = (ToolStripMenuItem)sender;
dataGridView.Columns[item.Text].Visible = item.Checked;
}
Subscribe the DataGridView on the event:
dataGridView.ColumnHeaderMouseClick += DataGridView_ColumnHeaderMouseClick;
In this event handler, show the menu.
private void DataGridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
columnMenu.Show(MousePosition);
}
I‘ve one ContextMenuStrip attached to two controls (DataGridView).
In the ToolStripMenuItem click event, currently I’ve used:this.ActiveControl.Name to get the active GridView control name;
This is fine if I first select the GridView cell and than Rt. click on it to invoke the ContextMenu
Case: sometime if GridView control is not a active control and cell is pre-selected, than context menu Item click not worked accordingly.
Is there any way to get the owner name that initiate the context menu Item click event?
Currently, In the ToolStripMenuItem click event, I've manage to get the original caller (i.e. DataGridView) with this code:
private void CopytoolStripMenuItem1_Click(object sender, EventArgs e)
{
var grid = new DataGridView();
switch (this.ActiveControl.Name)
{
case "dGVEL1":
{
grid=dGVEL1;
break;
}
case "dGVEL2":
{
grid=dGVEL2;
break;
}
}
if (grid == null) return;
DataObject data = grid.GetClipboardContent();
Clipboard.SetDataObject(data);
}
Finally I've Resolved the issue..
The complete solution is
private void CopytoolStripMenuItem1_Click(object sender, EventArgs e)
{
ToolStripDropDownItem item = sender as ToolStripDropDownItem;
if (item == null) // Error
return;
ContextMenuStrip strip = item.Owner as ContextMenuStrip;
var grid = strip.SourceControl as DataGridView;
if (grid == null) // Control wasn't a DGV
return;
switch (grid.Name)
{
case "dGVEL1":
{
grid=dGVEL1;
break;
}
case "dGVEL2":
{
grid=dGVEL2;
break;
}
}
if (grid == null) return;
DataObject data = grid.GetClipboardContent();
Clipboard.SetDataObject(data);
}
Is there any way to get the owner name that initiate the context menu Item click event?
I worked off of the solution you found and simplified it down. Hopefully, this can help some future readers who are looking for a more general solution.
It looks like you go through quite a bit to get ahold of the ContextMenuStrip which is already being passed as sender (granted you will still need to cast it). The following is a more general solution to this issue and it's a bit more simple.
private void contextMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e) {
//Cast the parent as a context menu strip so we can access 'sourceControl' attribute
ContextMenuStrip strip = (ContextMenuStrip) sender;
var stripParent = strip.SourceControl;
//print the name of the 'parent' (Control that called the context menu)
System.Diagnostics.Debug.WriteLine("Called From: " + stripParent.Name);
}
I have a DataGrid that has RowDetailsVisibilityMode set to "VisibleWhenSelected" so that the RowDetailsTemplate displays when I select a particular row. What I want is to be able to toggle the RowDetails visibility when I click the row again. By default, the RowDetails will be visible so long as its selected, but I want to essentially "unselect" the selected row if I click it again.
After trying a lot of things, I've found a sort of wacky solution that at least makes it so that I can toggle the visibility when I click the row:
private async void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
await Task.Delay(1); //ensures DataGrid.SelectedIndex is what I just clicked, not the previous value
if (DataGrid.SelectedIndex == prevSelectedIndex) { //check if I'm clicking on what's already selected
DataGrid.SelectedIndex = -1; //collapses everything
}
prevSelectedIndex = DataGrid.SelectedIndex; //save current selected index
}
The delay makes sure that the SelectedIndex property is already updated when I check for the selected index. I wish I didn't have to use it - I would be happy to find a better solution.
The problem now, however, is that whenever I click anything in the RowDetails of the selected row, this event fires and the row details disappear again. I want to be able to somehow check that I only expand/collapse the row when I click the row itself, not when I click whatever's in the RowDetailsTemplate. Is that possible? I don't want to use an extra Expander button for each row.
I found a solution to my problem:
In my RowDetailsTemplate under the ContentControl, I added an EventSetter that captured the PreviewMouseDown event:
<EventSetter Event="PreviewMouseDown" Handler="ContentControl_PreviewMouseDown" />
The Handler for the event set a global variable that indicated whether I had clicked on what's in the ContentControl:
private void ContentControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ClickedRowDetails = true;
}
Finally, in my original code, I simply checked whether I had clicked on the ContentControl, or whether it was something else, which in this case, meant it was the row itself:
private async void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ClickedRowDetails = false; //default assume that we haven't clicked row details
await Task.Delay(1); //wait for other events to fire
if (!ClickedRowDetails) {
if (DataGrid.SelectedIndex == prevSelectedIndex )
{
DataGrid.SelectedIndex = -1;
}
prevSelectedIndex = DataGrid.SelectedIndex;
}
}
So now, I can successfully expand/collapse rows by simply clicking on them, and then I can manipulate whatever's in the RowDetailsTemplate without accidently collapsing the row. This was done without an Expander button.
I think a global boolean variable can do the toggle for you.
public bool Visible = false;
private async void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (Visible == false)
{
await Task.Delay(1); //ensures DataGrid.SelectedIndex is what I just clicked, not the previous value
if (DataGrid.SelectedIndex == prevSelectedIndex)
{ //check if I'm clicking on what's already selected
DataGrid.SelectedIndex = 1; //collapses everything
}
prevSelectedIndex = DataGrid.SelectedIndex; //save current selected index
Visible = true;
}
else
{
await Task.Delay(1); //ensures DataGrid.SelectedIndex is what I just clicked, not the previous value
if (DataGrid.SelectedIndex == prevSelectedIndex)
{ //check if I'm clicking on what's already selected
DataGrid.SelectedIndex = -1; //collapses everything
}
prevSelectedIndex = DataGrid.SelectedIndex; //save current selected index
Visible = false;
}
}
I've got a ListBox with a bunch of items in it. The user can click an item to edit its contents. How do I prevent the user from deselecting all items? i.e., the user shouldn't be able to have nothing selected.
There is a case missing in your situation, which is when the list is cleared you will reselect an item there is no longer on the list. I solve this by adding an extra check.
var listbox = ((ListBox)sender);
if (listbox.SelectedItem == null)
{
if (e.RemovedItems.Count > 0)
{
object itemToReselect = e.RemovedItems[0];
if (listbox.Items.Contains(itemToReselect))
{
listbox.SelectedItem = itemToReselect;
}
}
}
I then put this inside a behaviour.
I'm not sure if there is a direct way to disable deselecting an Item, but one way which would be transparent to the user is to keep track of the last selected Item, and whenever the SelectionChanged event is raised and the selected index is -1, then reselect the last value.
This Works for Sure to Prevent User from Deselect... Add those 2 Events to your checkedListBox1 and set the Property CheckOnClick to "True" in Design Mode. (MSVS2015)
private void checkedListBox1_SelectedValueChanged(object sender, EventArgs e)
{
checkedListBox1.SetItemChecked(checkedListBox1.SelectedIndex, true);
}
private void checkedListBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
checkedListBox1.SetItemChecked(checkedListBox1.SelectedIndex, true);
}
To disable on or more options in your listbox/dropdown, you can add the "disabled" attribute as shown below. This prevent the user from selection this option, and it gets a gray overlay.
ListItem item = new ListItem(yourvalue, yourkey);
item.Attributes.Add("disabled","disabled");
lb1.Items.Add(item);
One solution, as suggested by amccormack:
private void hostsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(lstHosts.SelectedItem == null)
{
if(e.RemovedItems.Count > 0)
{
lstHosts.SelectedItem = e.RemovedItems[0];
}
alt text http://img413.imageshack.us/img413/9417/snapshotapp.jpg
The ContextMenuStrip tied to the ListView control. However, the right click option (edit)
appear where ever i click on the ListView area, this gives me exceptional error because the implementation of edit can only cope with a selected row. I only want it to appear when on a selected row (blue highlighted row). How can i do it?
Reset the ContextMenuStrip property back to (none). Implement the MouseUp event handler and use ListView.HitTest() to find out where it was clicked. For example:
private void listView1_MouseUp(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Right) {
var loc = listView1.HitTest(e.Location);
if (loc.Item != null) contextMenuStrip1.Show(listView1, e.Location);
}
}