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

I am trying to generate some xaml for a data form programmatically using a string. I can get the combo box. but when I try to use code defining a "MouseLeftButtonUp" or "Loaded" event handler in a string; after entering it, the page will turn white (without a noticeable error). See the corresponding 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; 
+7
c # silverlight xaml
source share
4 answers

Event handlers connected in XAML must be declared in code attached to the XAML file. In the case of ResourceDictionary or something downloaded from XamlReader.Load, there can be no code, so event handlers cannot be installed in XAML. The easiest way to get around this limitation would be to not create your template from strings and simply declare it in the Resources section of your XAML file, which you can do as follows:

Resources["MyTemplate"] as DataTemplate

to get the template and assign it in code like you are here, or just use StaticResource in XAML. As long as it remains in the same XAML file connected to this code, the event handlers that you have should now work fine. The dynamic portion of strings must also be modified to use Bindings.

If you want to stick with the XamlReader method, you have 2 problems to solve.

  • Locate the ComboBox instance inside the generated template.
  • Wait until the template is displayed to view the ComboBox

To find a ComboBox, you first need to assign it the x: Name attribute in the template text (you can simply replace the event code for the time being). Then you will need to find the element in the visual tree by name. It is quite simple, and here you can find it here .

To call this code at the right time, you need to either override OnApplyTemplate, which, unfortunately, will not work if you use something like UserControl, or use a different trick so that it does not work until all the controls are displayed. Here is a complete example that could go in the constructor and use 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 may have to wait to go to the editing state before it displays, in which case you will need to disconnect when the event is connected and find some other event in your data form that will happen when the state is changed.

+1
source share

One solution is to process the BeginningEdit DataForm event and use it to subscribe to the ComboBox MouseLeftButtonUp event MouseLeftButtonUp .

To do this, add a hidden field named isEventWiredUp to your code. We will use this field to track whether we have subscribed to an event and have not allowed to subscribe to it more than once.

Then add the x:Name="..." attribute to your ComboBox . We use this name to access drop-down lists.

Once this is done, add the following two methods that should do what you want. Replace yourComboBoxName with x:Name , which you specified in your combo box:

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

Sign the first of these two methods in the DataForm BeginningEdit event.

I have to admit that I was not able to fire the MouseLeftButtonUp event to fire the ComboBox. I'm not sure why this is happening, but it seems to be a common problem with ComboBox and not what happens due to how you create XAML. However, I managed to handle the event for the ComboBox SelectionChanged event.

I also tried replacing the Dispatcher.BeginInvoke string with a direct call to the OnBeginEdit method, but I found that this approach did not work. Events were not connected correctly; again, I'm not sure why.

+1
source share

Instead of directly attaching an event, you can use interactivity to bind events

eg.

 ... 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 may need to use ancestor binding to jump to the context in which your handler is defined. I am using a custom implementation of InvokeCommandAction; basically a copy of System.Windows.Interactivity.InvokeCommandAction, but extended so that it passes the event arguments to the command, you can do the same.

0
source share

XamlReader.Load is not allowed to attach eventHandlers to it. so use this technique to dynamically attach eventHandlers to it.

1- Write the Xaml line without eventHandlers -But write the Name property of these controls.

2- Download the string using XamlReader.Load(str);

3- Then load the contents of the DataTemplate from it. using Grid template = ((Grid)(dt.LoadContent()));

Note: here, the Grid is the parent control in the DataTemplate .

4- Locate the control by the name to which you want to attach the event handler. Button img = (Button)template.FindName("MyButtonInDataTemplate");

Hope this helps.

0
source share

All Articles