Handle all elements Holding event - c#

I have an idea of implementing holding event handler for all text-type controls ( label, textbox, passwordbox, hyperlink etc. ) in order to show message popup for allowing suggest a better translation of this text.
Is it possible to implement such type of event handler for all controls that get added/removed from/to visual tree.
Without doing such for each element:
<TextBox Holding="HoldingEventHandler"/>
I have tried this in my MainPage.xaml.cs:
AddHandler(Control.HoldingEvent, new RoutedEventHandler(HoldingOccured), true);
But it fails with exception:
Value does not fall within the expected range.

If you implement Holding in xaml and ask for new handler the system will generate the follow callback.
private void WindowsPage_Holding(object sender, HoldingRoutedEventArgs e)
{
}
So, thiis is a correct call parameters.
AddHandler(Control.HoldingEvent, new HoldingEventHandler(HoldingOccured), true);

Related

Event handlers when using a string as a data template for dataform in Silverlight

I am attempting to form some xaml for a dataform programmatically using a string. I can get the combo box to appear. but when I attempt to use the code specifying the "MouseLeftButtonUp" or the "Loaded" event handler in the string; the page will turn white (no noticeable error) out after going into it. Please see relevant code below.
StringBuilder editTemplate = new StringBuilder("");
editTemplate.Append("<DataTemplate ");
editTemplate.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' ");
editTemplate.Append("xmlns:toolkit='http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit' ");
editTemplate.Append("xmlns:navigation='clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation' ");
editTemplate.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' >");
editTemplate.Append("<StackPanel>");
editTemplate.Append(#" <toolkit:DataField Label='" + GetFieldWithoutNumber(theInfo, theDataContext) + "'>");
/* Won't Work */ editTemplate.Append(#" <ComboBox MouseLeftButtonUp='ComboBox_MouseLeftButtonUp' />");
/* Will Work */ editTemplate.Append(#" <ComboBox />");
editTemplate.Append(#" </toolkit:DataField>");
editTemplate.Append("</StackPanel></DataTemplate>");
dynamicDataForm.EditTemplate = XamlReader.Load(editTemplate.ToString()) as DataTemplate;
Event handlers hooked up in XAML are required to be declared in the code-behind connected to the XAML file. In the case of a ResourceDictionary or anything loaded from XamlReader.Load there can't be any code-behind, so event handlers can't be set in the XAML. The easiest way to get around this restriction would be to not build your template from strings and just declare it in the Resources section of your XAML file which you can then do like:
Resources["MyTemplate"] as DataTemplate
to get the template and assign it in code like you're doing here, or just use StaticResource in XAML. As long as it stays in the same XAML file connected to this code the event handlers you have in it currently should work fine. The dynamic part of the strings would also need to be changed to use Bindings.
If you want to stick with the XamlReader method you have 2 problems to solve.
Locate the ComboBox instance inside the rendered template
Wait until the template is rendered to look for the ComboBox
To find the ComboBox you need to first give it an x:Name attribute in the template text (you can just replace the event code currently there). Next you need to be able to locate an item in the visual tree by name. This is fairly straightforward and you can find an example here to do that.
To call this code at the right time you either need to override OnApplyTemplate, which unfortunately won't work if you're in something like a UserControl, or use another trick to keep it from running until all the controls are rendered. Here's a full example that could go in a constructor and uses the find method linked from above:
DataTemplate template = Resources["MyTemplate"] as DataTemplate;
dynamicDataForm.ContentTemplate = template;
Dispatcher.BeginInvoke(() =>
{
ComboBox button = FindVisualChildByName<ComboBox>(this, "MyControl");
if (button != null)
button.MouseLeftButtonUp += (s, _) => MessageBox.Show("Click");
});
In your case it looks like your template might need to wait to switch to an edit state before it renders in which case you'd need to hold off on connecting the event and find some other event on your dataform that happens when that state is changed.
One solution is to handle the BeginningEdit event of the DataForm and use that to subscribe your event handler to the ComboBox's MouseLeftButtonUp event.
To do this, add to your code-behind a private field named isEventWiredUp. We'll use this field to keep track of whether we've subscribed to the event and prevent the event from being subscribed to more than once.
Next, add an x:Name="..." attribute to your ComboBox. We use this name to get at the combobox.
Once that is done, add the following two methods, which should do what you want. Replace yourComboBoxName with the x:Name you gave to your combobox:
private void dynamicDataForm_BeginningEdit(object sender, CancelEventArgs e)
{
Dispatcher.BeginInvoke(OnBeginEdit);
}
private void OnBeginEdit()
{
if (!isEventWiredUp)
{
var combobox = dynamicDataForm.FindNameInContent("yourComboBoxName") as ComboBox;
if (combobox != null)
{
combobox.MouseLeftButtonUp += combobox_MouseLeftButtonUp;
isEventWiredUp = true;
}
}
}
Subscribe the first of these two methods to the DataForm's BeginningEdit event.
I have to admit that I was unable to get the MouseLeftButtonUp event to fire on the ComboBox. I'm not sure why this happens, but it seems to be a general problem with the ComboBox as opposed to something that happens because of the way you're constructing XAML. I was able to get an event handler for the ComboBox's SelectionChanged event to work, however.
I also tried replacing the Dispatcher.BeginInvoke line with a direct call to the OnBeginEdit method, but I found that this approach didn't work. The events weren't quite wired up correctly; again, I'm not sure why.
Rather than trying to hookup the event directly, you can use interactivity to bind up your events
e.g.
...
editTemplate.Append("xmlns:i='clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity' ");
...
editTemplate.Append(#"
<ComboBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName='MouseLeftButtonUp'>
<i:InvokeCommandAction Command='{Binding DataContext.YourCommand,
RelativeSource={RelativeSource AncestorType=XXX}}'
CommandParameter='{Binding}'/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>");
you might have to use some ancestor binding to get to the context on which your handler is defined. I use a custom implementation of InvokeCommandAction; basically a copy of System.Windows.Interactivity.InvokeCommandAction but extended so that it will pass the event args to the command, you might want to do the same.
XamlReader.Load not allowed to attach eventHandlers in it.
so use this technique to dynamically attach the eventHandlers to it.
1- Write your Xaml string without eventHandlers -But write the Name property of those Controls.
2- Load the string with XamlReader.Load(str);
3- Then load the content of DataTemplate from it. using Grid template = ((Grid)(dt.LoadContent()));
Note: here Grid is the parent Control in DataTemplate.
4- Find the Control by Name you want to attach the Event Handler.
Button img = (Button)template.FindName("MyButtonInDataTemplate");
I hope it helps.

Winforms Designer: How to disable my usercontrol from being a container?

I have a relatively simple setup. I have a custom usercontrol that has a bunch of components on it, some text boxes, and a listview.
In the designer, I can drag and drop other controls into my usercontrol, and it adds them to the usercontrol instance. I don't want this.
How can I explicitly say "Don't allow additional controls to be added to this usercontrol?"
That's not the way it works. When you drop your user control on a form then adding controls to it isn't supported. That requires a special designer, this answer shows what is required. Maybe it looks like the controls get added but they merely overlap your user control. Their parent is still the form.
If a programmer opens your user control class itself in the designer then, sure, he can add controls as he pleases. The only way to stop that is to not ship the source code and use the sealed keyword to prevent deriving from it.
You could create a boolean property MyContainer.DisableAddControls or something.
If your MyContainer.Controls.Add(..) is overridden, then you can throw some custom exception in that Add() method as follows:
if(DisableAddControls)
{
throw new DisableAddControlsException();
}
If you are inheriting that method straight from ContainerControl, then you can handle the ControlAdded event and throw the exception there.
myContainer.ControlAdded += myContainerControlAdded;
private void Control_Added(object sender, System.Windows.Forms.ControlEventArgs e)
{
if(DisableAddControls)
{
throw new DisableAddControlsException();
}
}
On second thought, this won't throw out your designer at design time... nevermind.

Event handler firing for all controls instead of individually

I am having a rather odd problem with the Gecko Webbrowser control, I have created my own class which inherits off of the Gecko Webcontrol and within the constructor of this I have set an event:
class fooGeckoClass: Gecko.GeckoWebBrowser
{
public fooGeckoClass()
{
this.DomClick += new EventHandler<Gecko.GeckoDomEventArgs>(fooEventFunction);
}
private static void fooEventFunction(Object sender, Gecko.GeckoDomEventArgs e)
{
((Gecko.GeckoWebBrowser)sender).Navigate("www.foo.com");
}
}
I am using three of these controls in a manually created UserControl, the controls are loaded in dynamically at start up from a config file and added the the UserControl controls collection. When clicking on any of the three controls, all three will navigate to "www.foo.com" away from there original site. I had a look at:
e.StopPropagation();
Which specifies that it stops further propagation of events during an event flow, however it does also specify that it will handle all events in the current flow, I believe the events must have already been given to the controls before this has a chance to stop it as the the three controls will still fire the event. I also tried e.Handled = true to no avail. Has anyone encountered this problem before and have any kind of solution to make it only fire on the control that was clicked on?
EDIT:
It may be worth showing how the controls are added to the form seeing as this must be where the problem is occurring (it does not happen if the controls are just placed in a user control in a small test app).
private void fooUserControl_Load(object sender, EventArgs e)
{
if (!this.DesignMode)
{
for (int iControls = 0; iControls < geckObs.Count(); iControls ++)
{
fooGeckoClass geckControl = new fooGeckoClass();
this.Controls.Add(geckControl );
break;
}
}
}
Odd answer but I seem to have resolved the issue, DomClick was being called at first run, changing to DomMouseClick or DomMouseUp has completely resolved the issue. I assume DomClick must be an event unto itself as it also doesn't use the GeckoDomMouseEventArgs but the regular GeckoEventArgs.
EDIT:
To add to this, the site I was going to was actually calling DomClick when it had finished loading hence the reason it was being called at start up across all three browsers.

Where to find TreeListViews ColumnHeaderClick Event?

I'm developing a tool which shows data from a database in a hierarchical manner. As there are additional data for each item I'm using a TreeListView control to display them in additional columns. The number of columns is determined by user input.
The custom control that I'm using is Ricciolos TreeListView:
http://windowsclient.net/blogs/ricciolocristian/archive/2008/03/22/a-complete-wpf-treelistview-control.aspx
My problem now is, that I need to catch the ColumnHeaderClick event to apply a sorting logic. I already interviewed auntie Google, but no results.
Maybe somene here knows where to find such an event and how to determine which column header has been clicked.
Thanks
You would need to add a handler for the GridViewColumnHeader.Click event. This post describes how to do it for the ListView, which uses the same underlying controls. This code was adapted from that link:
myTreeListView.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(this.OnGridViewColumnHeaderClicked));
private void OnGridViewColumnHeaderClicked(object sender, RoutedEventArgs e) {
MessageBox.Show("testing");
}
Alternatively, you can attach a handler via XAML like so:
<my:TreeListView GridViewColumnHeader.Click="OnGridViewColumnHeaderClicked" />
The e.OriginalSource will include the GridViewColumnHeader, and e.Source/sender would be the TreeListView.

How to add event handler programmatically in WPF like one can do in Winform

What's the equivalent of this winform instruction:
this.button1.Click += new System.EventHandler(this.button1_Click);
in WPF ?
Update: also for a slider. And what namespace should I declare ?
My point is NOT to use XAML.
Something like this...
this.btnTest.Click+=new RoutedEventHandler(btnTest_Click);
Try this:
button1.AddHandler(Button.ClickEvent, new RoutedEventHandler(button1_Click));
then you have to create a function like this
void button1_Click(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}
If you're adding the new event from the XAML side, the IDE does most of the work for you.
For instance, if you type
<Button Name="button1" Click=
then the IDE will pop up with a drop-down of all your currently created events.
You can select one of your previously-created events, or create a new one by selecting "New Event Handler"
If you select "New Event Handler", then VS automatically adds the skeleton for you in the form.xaml.cs C# code-behind. You just add whatever you'd like the click event to do inside the already-made skeleton.
WPF isn't a new language, i.e., it's exactly the same concept. The only thing that may change is the delegate type. So yeah, you would do it the same way.

Categories