cannot retrieve object of TreeViewItem - silverlight

I have a tree view in silver light which i am creating dynamically from my code behind on the load event of my .xaml page. My treeview contains numerous treeviewitems.The header of my treeviewitem contains a stack panel. My stackpanel contains a check box as child. I have created an event handler for Unchecked event of the check box.
Now here is my problem.
When the unchecked event of my check box is triggered i want to retrieve the object of treeviewitem that is consuming the check box.
Here is the code snippet that shows how i am creating my treeviewitem
objTreeviewItem = new TreeViewItem();
objStackPanel = new StackPanel();
objStackPanel.Orientation = Orientation.Horizontal;
objCheckBox = new CheckBox();
objCheckBox.Content = "Checkbox1";
objCheckBox.Unchecked += new RoutedEventHandler(objCheckBox_Unchecked);
objStackPanel.Children.Add(objCheckBox);
objTreeviewItem.Header = objStackPanel;
Here is the code snippet for unchecked event of the checkbox
void objCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
try
{
TreeViewItem objItem = (((e.OriginalSource) as CheckBox).Parent as StackPanel).Parent as TreeViewItem;
}
catch (Exception ex)
{
}
}
The above statement in the try block is returning me a null value. Hence i am not able to retrieve the treeviewitem which is consuming the check box on which event has been triggered.
So is there any other Property or method(other then the parent property) which can help me get the treeviewitem.
Please any kind of help will be appreciated

The OriginalSource property does not return your CheckBox but whatever control on which the event started which may be a TextBlock inside the CheckBox, cast the sender instead, it will always be the control the event is associated with.

Related

dynamic tooltips on Winforms controls

I am looking for the cleanest way to bind the same datasource to a control's tooltip that I am binding to the control itself. For example, I have the line
control.DataBindings.Add(new Binding("EditValue", dataFeatures, "Key", true));
where dataFeatures is of type BindingSource. I repeat similar lines for many controls on a WinForm Form. Some of these controls can adopt values whose text can span a greater text width than what is visible within the control itself. Instead of redesigning the layout of the form to account for the possibility of partially hidden text in some controls in a few situations, I would like to have the tooltip of each control be bound to the same property of the BindingSource as the controls' EditValue or Text property. Is this possible? I can imagine there is a way to do it by hand by handling the EditValueChanged event like I already do for different reasons, but I was hoping there would be a cleaner solution than having to add new lines of code for each control.
Anybody have a suggestion?
Thanks!
0. For DevExpress controls you can just bind DevExpressControl.ToolTip property to the same value:
devExpressControl.DataBindings.Add(new Binding("EditValue", dataFeatures, "Key", true));
devExpressControl.DataBindings.Add(new Binding("ToolTip", dataFeatures, "Key", true, DataSourceUpdateMode.Never));
1. For standard WinForms controls you can use System.Windows.Forms.ToolTip component and its ToolTip.Popup event. For each control set its ToolTip to some value otherwise ToolTip will never appears:
control.DataBindings.Add(new Binding("Text", dataFeatures, "Key", true));
toolTip1.SetToolTip(control, "Some value");
Now you can use ToolTip.Popup event:
private bool _updatingToolTip;
private void toolTip1_Popup(object sender, PopupEventArgs e)
{
if (_updatingToolTip) return;
//Get binding for Text property.
var binding = e.AssociatedControl.DataBindings["Text"];
if (binding == null) return;
//Get binding value.
var manager = binding.BindingManagerBase;
var itemProperty = manager.GetItemProperties().Find(binding.BindingMemberInfo.BindingField, true);
object value = itemProperty.GetValue(manager.Current);
string toolTipText;
if (value == null || string.IsNullOrEmpty(toolTipText = value.ToString()))
{
e.Cancel = true;
return;
}
//Update ToolTip text.
_updatingToolTip = true;
toolTip1.SetToolTip(e.AssociatedControl, toolTipText);
_updatingToolTip = false;
}
You can easily implement dynamic tooltips with the ToolTipController component. Put this component onto the Form, and assign to each editor via the BaseControl.ToolTipController property.
When it is done, you can handle the ToolTipController.BeforeShow event and change the text according to the control state. The active control is passed through the SelectedControl property of the event parameter.

Find control that caused ContextMenuStrip menu to be shown

I've read a few articles on SO:
How to detrmine the control that cause ContextMenuStrip
Getting the control of a context menu
and a couple others that suggested use of the SourceControl property.. but none work in this context:
I have a ContextMenuStrip that has a child ToolStripMenuItem - this code from the windows forms designer generated section:
// _tileContextMenuStrip
//
this._tileContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.tileKindToolStripMenuItem,
this.forceWidthScalingToolStripMenuItem,
this.forceHeightScalingToolStripMenuItem});
this._tileContextMenuStrip.Name = "_tileContextMenuStrip";
this._tileContextMenuStrip.Size = new System.Drawing.Size(184, 70);
//
// tileKindToolStripMenuItem
//
this.tileKindToolStripMenuItem.Name = "tileKindToolStripMenuItem";
this.tileKindToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.tileKindToolStripMenuItem.Text = "Tile Kind";
So the context menu strip and the menu item first in the list are fixed at design time. At runtime, the TSMI has child TSMIs added to it in a loop based on an enum:
foreach(TileKind t in typeof(TileKind).GetEnumValues()) {
ToolStripMenuItem tsmi = new ToolStripMenuItem(t.ToString("g"));
tsmi.Tag = t;
tsmi.Click += tsmi_Click;
tileKindToolStripMenuItem.DropDownItems.Add(tsmi);
}
Later I have 20 checkboxes on my form and I set their .ContextMenuStrip to be the same thing:
foreach(Thing t in someDataSource){
CheckBox c = new CheckBox();
c.Text = t.SomeData;
c.ContextMenuStrip = this._tileContextMenuStrip;
myPanelBlah.Controls.Add(c);
}
Great, so now I have all my checkboxes and they all show the context menu when I right click them, but when I choose one the sub-menu items, I just can't find out the control that fired the context menu...
//this the click handler for all the menu items dynamically added
void tsmi_Click(object sender, EventArgs e)
{
ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
(tsmi.OwnerItem //the parent node in the menu tree hierarchy
.Owner as ContextMenuStrip) //it's a ContextMenuStrip so the cast succeeds
.SourceControl //it's always null :(
}
I can reliably get ahold of the contextmenustrip either by routing up from the event handler sender, or even just by referencing the ContextMenuStrip itself as a form instance variable, but SourceControl is always null
Any ideas what to try next?
I see the problem, quacks loudly like a bug. There's a workaround, you can subscribe the ContextMenuStrip's Opening event. At that point, well before you start navigating into the sub-items, the SourceControl property is still valid. So store it in a field of the class so you'll have it available in the Click event handler. Roughly:
private Control _tileCmsSource;
private void _tileContextMenuStrip_Opening(object sender, CancelEventArgs e) {
_tileCmsSource = _tileContextMenuStrip.SourceControl;
}
void tsmi_Click(object sender, EventArgs e)
{
ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
// Use _tileCmsSource here
//...
}

Silverlight DataGrid MouseLeftButtonDown event not raised when clicking on rows

Note: I have found a solution to my problem so I am posting this for reference purposes, although I would be happy to be educated with a better solution.
I'm trying to provide double click functionality on a Silverlight DataGrid by hooking into the UIElement.MouseLeftButtonDown but when I subscribe to the DataGrid.MouseLeftButtonDown using XAML or the DataGrid.MouseLeftButtonDown += syntax, my event handler is not called when I click on the rows within the DataGrid. If I click on the Header, the event is raised.
If I subscribe to the same event at the parent UserControl level, the event handler is called successfully as you would expect based on Silverlight RoutedEvents but then I have to detect whether the click occurred on the DataGrid or somewhere else.
If I subscribe to the event using this UIElement.AddHandler syntax, as shown below, then it works as expected based on the handledEventsToo: true parameter.
dataGrid.AddHandler(UIElement.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(dataGrid_MouseLeftButtonDown)
, handledEventsToo: true);
It seems that the DataGrid implementation is marking these events as handled, prevent event bubbling, by default in one of the child UIElements, which is not what I expected initially. With more thought I can see that the click behaviour drives all sorts of things (select item, edit field etc.) so perhaps the implementation makes sense.
I had the same problem and I used MouseLeftButtonUp which fires the event but the clickcount value is always 1.
Here is the fix for that:
private const int MOUSE_SENSITIVITY = 300;
private DateTime _previousClick;
private void exceptionsDataGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
DataGrid dg = (sender as DataGrid);
DateTime current=DateTime.Now;
LoggerService.Exception exception = (LoggerService.Exception)dg.SelectedItem;
if (_previousClick != null)
{
TimeSpan clickSpan = current - _previousClick;
if (clickSpan.TotalMilliseconds < MOUSE_SENSITIVITY)
{
MessageBox.Show("You double clicked!");
}
}
_previousClick = current;
}

"Intercept" the opening of any tooltip appwide

I want to show the text of a tooltip of any control in my wpf app inside a status bar, when a tooltip is about to be opened.
Of course I could try to loop recursively through all child controls of the main window and
set their ToolTipOpening event to always the same method. But is there an easier way ?
Something like a Application.Current.AnyToolTipOpening event ?
Sure, try this:
EventManager.RegisterClassHandler(typeof(FrameworkElement), FrameworkElement.ToolTipOpeningEvent, new ToolTipEventHandler(ToolTipHandler));
That registers a handler for all classes that derive from FrameworkElement.
Your handler method might look like this:
private void ToolTipHandler(object sender, ToolTipEventArgs e) {
// To stop the tooltip from appearing, mark the event as handled
e.Handled = true;
FrameworkElement source = e.Source as FrameworkElement;
if (source != null) {
MessageBox.Show(source.ToolTip.ToString()); // or whatever you like
}
}
thanks , that worked. Additionally,to make the statusbar text disappear when the mouse leaves the control with the tooltip :
EventManager.RegisterClassHandler(typeof(FrameworkElement),
MouseLeaveEvent, new MouseEventHandler(ClearText));

select tabItem programmatically in WPF

I have different tabItems in a TabControl
and each tabItem has some input fields.
I am moving between the tabItems programmatically (like a wizard to move from the first to the next)
I am using this code inside the "Next" button
tabItem2.isSelected = true;
my problem that when I move between the tabItems by clicking on them, the focus (keyboard focus) will move to the first textbox input.
But programmatically with the previous code, the focus won't move to the first input textbox item inside the tabItem.
Any idea?
If you're forcing the IsSelected property, I'd also give the first TextBox a name and set the focus after you set the selected tab.
If you're building your UI dynamically, this won't work, but you can create a utility method which searches the logical tree (or the visual tree if you're using presenters/view-models) for the first input control and then set the focus.
These solutions didn't work for me. It got as far selecting the TabItem I wanted, but it wasn't able to select/focus the desired TreeViewItem. (It would only focus the TVI if the TabItem was already selected.) The solution below finally worked for me.
(FYI: The snippets below are part of app that is similar to Microsoft Help Viewer 2.0. When you click the "Sync" button, it first selects the Contents tab if not already selected, then traverses into tree view until it finds the matching tree view item. Which it then selects/focuses.)
Cheers
private void OnClick_SyncContents(object sender, RoutedEventArgs e)
{
// If the help-contents control isn't visible (ie., some other tab is currently selected),
// then use our common extension method to make it visible within the tab control. Once
// it visible, the extension method will call the event handler passed (which is this method)
if (!this.m_UcHelpFileContents.IsVisible)
{
this.m_UcHelpFileContents.
SelectParentTabItem_WaitForMeToBecomeVisible_ThenCallThisEventHandlerWithNullArguments
(this.OnClick_SyncContents);
}
else
{
// Else the help-contents control is currently visible, thus focus the
// matching tree view item
/* Your code here that focuses the desired tree view item */
}
}
public static class CommonExtensionMethods
{
public static void
SelectParentTabItem_WaitForMeToBecomeVisible_ThenCallThisEventHandlerWithNullArguments
(this FrameworkElement frameworkElement, RoutedEventHandler eventHandlerToCallWhenVisible)
{
// First, define the handler code for when the given framework element becomes visible
DependencyPropertyChangedEventHandler HANDLER = null;
HANDLER = (s, e) =>
{
// If here, the given framework element is now visible and its tab item currently selected
// Critical: first and foremost, undo the latch to is-visible changed
frameworkElement.IsVisibleChanged -= HANDLER;
// Now invoke the event handler that the caller wanted to invoke once visible
frameworkElement.Dispatcher.BeginInvoke(eventHandlerToCallWhenVisible, null, null);
};
// Use our common extension method to find the framework element's parent tab item
TabItem parentTabItem = frameworkElement.GetFirstParentOfType<TabItem>();
if (parentTabItem != null)
{
// Assign the handler to the given framework element's is-visible-changed event
frameworkElement.IsVisibleChanged += HANDLER;
// Now set the tab item's is-selected property to true (which invokes the above
// handler once visible)
parentTabItem.IsSelected = true;
}
}
public static T GetFirstParentOfType<T>
(this FrameworkElement frameworkElement) where T : FrameworkElement
{
for (FrameworkElement fe = frameworkElement.Parent as FrameworkElement;
fe != null;
fe = fe.Parent as FrameworkElement)
{
if (fe is T)
return fe as T;
}
// If here, no match
return null;
}
}

Resources