TreeView Event keeps firing - c#

I'm using the WPF version of TreeView (System.Windows.Controls.TreeView) and need to assign several events. As these are WPF most of the events are either different or behave in a different way hence my confusion as AfterExpand is not available
For now I need 2 events:
SelectedItemChanged which is at TreeView level
ExpandedEvent which is at TreeViewItem level
So far I have
private void DisplayGetEventTypes(UXEvent.GetEventTypesResp resp, CustomAsyncStateContainer state)
{
navBarControl.Groups.Clear();
if (resp.eventTypeItems != null)
{
UXEvent.EventType[] eventItems = resp.eventTypeItems;
int nodeCount = eventItems.Length;
for (int i = 0; i < nodeCount; i++)
{
UXEvent.TryEvent eventItem = new UXEvent.TryEvent();
eventItem.eventName = eventItems[i].name;
eventItem.eventId = eventItems[i].id;
NavBarGroup group1 = new NavBarGroup();
group1.Header = eventItems[i].name;
group1.Tag = eventItem;
group1.IsExpanded = false;
//Add dummy treeview to fill later if expanded
System.Windows.Controls.TreeView treeview = new System.Windows.Controls.TreeView();
treeview.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(myTreeView_SelectedItemChanged);
AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
group1.Content = treeview;
group1.DisplaySource = DisplaySource.Content;
navBarControl.Groups.Add(group1);
}
}
}
and the following two draft event handlers for testing
void myTreeView_SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> e)
{
// Set the data context of the text block to the selected value.
var node = (ItemsControl)e.OriginalSource;
System.Windows.Forms.MessageBox.Show("Selected!");
}
void myTreeView_ItemExpanded(object sender, RoutedEventArgs e)
{
// Set the data context of the text block to the selected value.
var node = (TreeViewItem)e.OriginalSource;
System.Windows.Forms.MessageBox.Show("Opening! - " + node.Header);
}
The problem I'm having is that myTreeView_ItemExpanded is firing multiple times when any treeviewItem is expanded. I think it fires multiple times due to the event bubbling .
Firstly, can anyone point me to some good resources where I can read up on this behaviour?
Secondly, how can I amend my code to make AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded)); a single event only?
Thank you

I spotted the problem. The line
AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
should read
treeview.AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
I assume the first line must have added the event handler to all available controls and all fired when the event triggered.

Related

howto pass dictionary key in a form event C#?

Here is the code that I am running:
private Dictionary<string, List<GuiEvent>> m_events = new Dictionary<string, List<GuiEvent>>();
private void OnRadioBtnCheckedChange(object sender, EventArgs e, string formhandle)
{
RadioButton control = (RadioButton)sender;
GuiEvent evnt = new GuiEvent
{
id = GuiEventType.RadioButtonChange,
ElementName = control.Name,
sparam = control.Text,
lparam = control.Checked ? 1 : 0
};
m_events[formhandle].Add(evnt);
}
Getting the error:
Severity Code Description Project File Line Suppression State
Error CS0123 No overload for 'OnRadioBtnCheckedChange' matches delegate 'EventHandler' MtGuiController C:\Users\AIMS-RESEARCH\Desktop\MtGuiController\MtGuiController\Controller.cs 258 Active
Previously when the m_event was declared like this:
private List<GuiEvent> m_events = null;
And the function:
private void OnRadioBtnCheckedChange(object sender, EventArgs e)
{
RadioButton control = (RadioButton)sender;
GuiEvent evnt = new GuiEvent
{
id = GuiEventType.RadioButtonChange,
ElementName = control.Name,
sparam = control.Text,
lparam = control.Checked ? 1 : 0
};
m_events.Add(evnt);
}
Everything was working fine.
I am not able to understand what I can do in this situation. I cannot declare string formhandle as global as it is changing every time. So please take a note of it. It is C# DLL function.
Can anybody tell me solution that will help?
If you want to use some additional information in your event handler, you cannot pass it as an additional parameter to event handling function. You cannot also change type of event handler parameters - it should just accept two arguments - sender and EventArgs.
You have three options here. First - store data in sender and then access that data in event handler. Simplest way is using radio button's tag. Control.Tag is inherited from base Control class and you can use it to store some data when you create control. I.e. assign appropriate formhandle to every radio button Tag (you can even do that manually via designer)
radioButton1.Tag = "foo";
radioButton2.Tag = "bar";
and then retrieve it in event handler:
private void OnRadioBtnCheckedChange(object sender, EventArgs e)
{
RadioButton control = (RadioButton)sender;
GuiEvent evnt = new GuiEvent
{
id = GuiEventType.RadioButtonChange,
ElementName = control.Name,
sparam = control.Text,
lparam = control.Checked ? 1 : 0
};
var formhandle = (string)control.Tag; // here you get "foo" or "bar"
m_events[formhandle].Add(evnt);
}
Second option - create your custom radio button control. But that's overkill here. Usually, you do that when you need a custom look & feel, not just to pass data.
Third option - lookup data you want using the data you have (e.g. control name). This option makes sense when data you need is not available at the time of control creation or if it changes over time.

How can you capture UWP pointer events on Popups/Tooltips/TeachingTip

I'm using UWP's TeachingTip and I need to know when the pointer is over the control.
This is how I create and insert the TeachingTip:
var teachingTip = new TeachingTip
{
IsOpen = true,
Title = "hello",
Subtitle = "world"
};
var mainPage = (Window.Current.Content as Frame)?.Content as MainPage;
var content = (Windows.UI.Xaml.Controls.Canvas)mainPage.Content;
content.Children.Add(teachingTip);
I tried this but didn't get any event:
teachingTip.PointerEntered += TipPointerEntered;
teachingTip.PointerExited += TipPointerExited;
...
I also tried this but that didn't make any difference:
teachingTip.AddHandler(UIElement.PointerEnteredEvent, new PointerEventHandler(TipPointerEntered), true);
Then i read here this:
If you want to handle routed events from a Popup or ToolTip, place the handlers on specific UI elements > that are within the Popup or ToolTip and not the Popup or ToolTip elements themselves.
So my best guess is that a TeachingTip behaves the same. But I couldn't figure out how to gain access to the TeachingTip's children.
So I tried a different approach, maybe I could track the pointer once the tip is open and do some hit testing on the control. So I tried this:
var mainPage = (Window.Current.Content as Frame)?.Content as MainPage;
mainPage.AddHandler(UIElement.PointerMovedEvent, new PointerEventHandler(PointerMoved), true);
void PointerMoved(object sender, PointerRoutedEventArgs e)
{
var page = (Window.Current.Content as Frame)?.Content as MainPage;
var currPoint = e.GetCurrentPoint(page);
var elements = VisualTreeHelper.FindElementsInHostCoordinates(new Windows.Foundation.Point(currPoint.Position.X, currPoint.Position.Y), teachingTip);
foreach (UIElement element in elements)
{
// Eureka! found an element
}
}
but no eureka... if anyone can help...
Eventually I revisited this (a couple of years later...).
The solution is pretty simple, after applying a template you need to get the root element on which you want to register for pointer positions, like this:
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var rootGrid = this.GetTemplateChild("ContentRootGrid") as Windows.UI.Xaml.Controls.Grid;
if (rootGrid != null)
{
rootGrid.PointerEntered += (sender, e) =>
{
SetPointerPosition(PointerPosition.Popup);
};
rootGrid.PointerExited += (sender, e) =>
{
SetPointerPosition(PointerPosition.Page);
};
}
}

ItemContainerGenerator.ContainerFromIndex returns null after Insert

I have a ListBox in a Windows Phone app. In a button action I need to set a transformation and name on every ListBoxItem in the ListBox called lb.
My datasource is
var items = new ObservableCollection<string>();
for (int i = 0; i < 10; ++i)
{
items.Add("Item " + i);
}
lb.ItemsSource = items;
I have a code to add a RenderTransform to each ListBoxItem in the ListBox
for (int i = 0; i < items.Count;++i )
{
var item = this.lb.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
item.RenderTransform = new CompositeTransform();
item.Name = i.ToString() //needed for storybord
//another stuff
}
and it works ok. The problem is that I first need to insert and item to the list. When I call items.Insert(index,"test") before the for loop I get an exception that the item is null when i==index. It does not matter when I insert the new item, I always get null for that item.
What am I doing wrong? Or is there an event of the ListBox I need to wait for when I insert the new item before trying to acces the ListBoxItem?
Edit: I extracted the code and put it into a solution: https://dl.dropboxusercontent.com/u/73642/PhoneApp2.zip. I first insert a fake item to the new solution, the fade it away and move the original item to that position using an animation.
Right after item added there is not container generated because of asynchronous nature of UI subsystem. Try subscribing on the ItemsChanged (or StatusChanged, sorry i don't remember) and get item when event is fired with proper event args.
Waiting for the Dispatcher to finish doing what its doing such as (updating the UI because of a new item being added)
this.Dispatcher.BeginInvoke(() =>
{
//Code Here
});
If you ever manipulate the UI such as adding an item to a listbox without the UI getting updated, you will not be able to run code targeting the UI.
Edit: Here is the code for your project to get working
private void Button_Click(object sender, RoutedEventArgs e)
{
start = Int32.Parse(from.Text);
end = Int32.Parse(to.Text);
fake = items[start];
//items.Insert(end, fake);
this.Dispatcher.BeginInvoke(() =>
{
for (int i = 0; i < items.Count; ++i)
{
var item = this.lb.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
item.Name = i.ToString();
}
(this.lb.ItemContainerGenerator.ContainerFromIndex(end) as ListBoxItem).RenderTransform = new CompositeTransform();
(this.lb.ItemContainerGenerator.ContainerFromIndex(end) as ListBoxItem).Name = "listBoxItem1";
(this.lb.ItemContainerGenerator.ContainerFromIndex(start) as ListBoxItem).Name = "listBoxItem";
sbListBox.Begin();
});
}

Adding/Removing events based on context

I am new to C# and WinForms. I have some objects with the right click (context menu) events. However, depending on the context (for example depending on whether the user is in the wizard screen or the main screen), I want to alter what the right click does. I googled a bit and found that I can use += and -= operators but I still could not achieve what I want to do. Given the code below, for example,
Any ideas ?
EDIT: I want the OnClickCard behave differently in different places.
Sammple Code:
public override ContextMenuStrip GetContextMenuStrip(GoView view)
{
if (Selectable)
{
ContextMenuStrip contextMenu = new ContextMenuStrip();
if (!Empty)
{
// this is just for example so not showing the implementation
contextMenu.Items.Add(new ToolStripMenuItem(
"Delete",
null,
new EventHandler(OnClickDelete)));
}
// Empty
else
{
ToolStripMenuItem addCard = new ToolStripMenuItem("Add");
foreach (..some data..)
{
ToolStripMenuItem card = new ToolStripMenuItem(
data,
null,
new EventHandler(OnClickCard));
addCard.DropDownItems.Add(card);
}
}
}
else
{
return null;
}
}
private void OnClickCard(object sender, EventArgs e)
{
ToolStripMenuItem cardItem = (ToolStripMenuItem)sender;
if (cardItem.Text.Contains("ABC"))
{
Common.Forms.FormMMUSettings f = new FormMMUSettings(cardItem.Text,ParentMagazine.NextSite);
f.Show();
}
SetCard(new MagazineCard(2, cardItem.Text));
}
Are you saying you want to change the contents of the context menu depending on the circumstances when it's clicked? If so, the easiest way is to create multiple context menus, and just use an event to set whichever context menu you want.
ContextMenu menu1 = new ContextMenu();
MenuItem menu1Item1 = new MenuItem();
menu1Item1.Header = "Menu 1 Item 1";
menu1Item1.Click += new RoutedEventHandler(menu1Item1Clicked);
menu1.Items.Add(mnu1Item1);
MenuItem menu1Item2 = new MenuItem();
menu1Item2.Header = "Menu 1 Item 2";
menu1Item2.Click += new RoutedEventHandler(menu1Item2Clicked);
menu1.Items.Add(menu1Item2);
ContextMenu menu2 = new ContextMenu();
MenuItem menu2Item1 = new MenuItem();
menu2Item1.Header = "Menu 2 Item 1";
menu2Item1.Click += new RoutedEventHandler(menu2Item1Clicked);
menu2.Items.Add(menu2Item1);
MenuItem menu2Item2 = new MenuItem();
menu2Item2.Header = "Menu 2 Item 2";
menu2Item2.Click += new RoutedEventHandler(menu2Item2Clicked);
menu2.Items.Add(menu2Item2);
public void menu1Item1Clicked(object sender, EventArgs e)
{
}
etc..
Now you can just set whichever menu you need using:
myForm.ContextMenu = menu1;
Hope this helps.
+= and -= should work just fine but I'd suggest you to use a kind of handlers repo where you'd switch which handler to use. This should work like a strategy pattern where different wizard steps would be different strategies.
It would be easier to help you if you'll show us some code and point us which code parts do not work properly.
You seem to suggest that you would like to attach different handlers depending on context:
if(that)
obj.event += HandleThat;
else
obj.event += HandleSomethingElse;
That should work, but you can also do it in one handler:
obj.event += HandleAll;
void HandleAll(object sender, EventArgs arg) {
if(that)
HandleThat();
else
HandleSomethingElse();
}
EDIT: ok, your edited question really meant something else.
First obvious problem is that you add OnClickDelete as a handler, and show the implementation of OnClickCard which will not be called in your example. If it is a typo, then you just need to implement the handler method as you need. What part exactly is not working?

InvalidArgument=Value of '0' is not valid for 'SelectedIndex'. Parameter name: SelectedIndex

I am getting the above error when i am trying this code. I tried giving just my code but no use. (It was default)
Here is my XML file
The error is in cmbProduct_SelectedIndexChanged event.
cmbProduct --> combobox
cmbBrand --> combobox
Global
DataSet dsUpdate = new DataSet();
Form_load
dsUpdate.ReadXml(#"...\..\stock.xml");
cmbProduct.DataSource = dsUpdate.Tables[0]
.DefaultView.ToTable(true, "productname");//.DefaultView;
cmbProduct.DisplayMember = "productname";
cmbProduct.SelectedIndex = 0;
cmbProduct_SelectedIndexChanged
cmbBrand.Items.Clear();
foreach (DataRow Row in dsUpdate.Tables[0].Select("productname='" + cmbProduct.Text + "'"))
{
//cmbBrand.SelectedIndex = i;
cmbBrand.Items.Add(Row["brandname"].ToString());
//i++;
}
cmbBrand.SelectedIndex = 0; /*ERROR*/
Please help
Thanks in Advance.
Problem is:
when you start application, you do not have items in cmbBrand, but cmbProduct fires SelectedIndexChanged.
Try this:
remove SelectedIndexChanged event initialization from Form1.Designer.cs. Try to find following line:
this.cmbProduct.SelectedIndexChanged += new System.EventHandler(this.cmbProduct_SelectedIndexChanged);
After that, when you populate DataSet with data from xml file, initialize SelectedIndexChanged event:
dsUpdate.ReadXml(#"...\..\stock.xml");
cmbProduct.DataSource = dsUpdate.Tables[0].DefaultView.ToTable(true, "productname");//.DefaultView;
cmbProduct.DisplayMember = "productname";
this.cmbProduct.SelectedIndexChanged += new System.EventHandler(this.cmbProduct_SelectedIndexChanged);
cmbProduct.SelectedIndex = 0;
i had same error. i think this error have a some reasons.
so my error is related to "set DataSource in another thread is not working"
example
//Run in another thread
myComboBox.DataSource = myDataSource; //not set
fix with
myComboBox.Invoke(new Action(() => myComboBox.DataSource = myDataSource));
You can also try this. Before setting combobox DataSource set its BindingContext
cmbProduct.BindingContext = this.BindingContext;
This will happen if you attempt to set the SelectedIndex while there is no valid datasource. If you're resetting the default to 0, and occasionally changing the datasource list, you may see this. You don't need to default to 0 if applying a new datasource, so simple check will avoid it happening:
if (comboBox.Datasource != null) comboBox.SelectedIndex = 0;
If you have this issue:
use the Form_Activated event handler to control setting the indexes.
For me, I had a series of dynamically generated ComboBoxes I added to a Form.
I made a list of the ones where I wanted to use SetIndex=0, then iterated through them in this handler.
I also had a boolean, firstFormActivation, when called the SetIndex the one time only..
You can incidentally use this method for Focus() too, so first field in a Form gets focus when dynamically added.
Here is some code to illustrate the point:
private readonly List<ComboBox> combosToSetIndexOn = new List<ComboBox>();
private bool firstActivation = true;
private Control firstWindowsControl = null;
...
// Other code sets firstWindowsControl...
private void DynamicForm_Activated(object sender, EventArgs e)
{
if (firstActivation)
{
firstActivation = false;
bool fwcPresent = (firstWindowsControl != null);
Console.WriteLine($"DynamicForm_Activated: firstWindowControl present: {fwcPresent}");
if (fwcPresent)
{
firstWindowsControl.Focus();
}
if (combosToSetIndexOn.Count > 0)
{
foreach (ComboBox c in combosToSetIndexOn)
{
Console.WriteLine($"DynamicForm_Activated: processing: {c.Name}");
c.SelectedIndex = 0;
}
}
}
In my case the following was causing my problem
myComboBox.DataSource = myBindingSource
myBindingSource.DataSource = items.ToList() // error
The following worked
myComboBox.DataSource = null;
myBindingSource.DataSource = items.ToList();
MyComboBox.DataSource = myBindingSource;

Categories

Resources