3

I am trying to add UI elements dynamically, but I am facing a problem, I able to add UI elements, but I fail to add click event to Button

Below is my code :

ParserContext context = new ParserContext();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");

string xaml = String.Format(" <StackPanel Orientation='Vertical'>");
xaml = xaml + "<StackPanel Orientation='Horizontal'>";
xaml = xaml + "<Button Margin='5,5,0,0'  Background='AliceBlue' Foreground='DarkBlue' Height='25' VerticalAlignment='Bottom' HorizontalAlignment='Right' Width='82'  Tag='12' Click='btnMy_Click'>";
xaml = xaml + "<StackPanel Orientation='Horizontal'>";
xaml = xaml + "<Image Source='/MotionTest;component/images/open.png' Width='18' Height='18' />";
xaml = xaml + "<TextBlock Text='  Open' VerticalAlignment='Center' FontSize='13' />";
xaml = xaml + "</StackPanel>";
xaml = xaml + "</Button>";
xaml = xaml + "</StackPanel>";
xaml = xaml + "</StackPanel>";

UIElement element = (UIElement)XamlReader.Parse(xaml, context);
myTestGrid.Children.Add(element);

And my onClick function :

private void btnMy_Click(object sender, RoutedEventArgs e)
{
    var myValue = ((Button)sender).Tag;
    MessageBox.Show("Here = " + myValue);
}

For this line :

 xaml = xaml + "<Button Margin='5,5,0,0'  Background='AliceBlue' Foreground='DarkBlue' Height='25' VerticalAlignment='Bottom' HorizontalAlignment='Right' Width='82'  Tag='12' Click='btnMy_Click'>";

If I remove the

Click='btnMy_Click'

It will work. But if I add it, It shows

enter image description here

Anyone know how to solve this ? Thanks in advance.

DIF
  • 2,470
  • 6
  • 35
  • 49
Anson Tan
  • 1,256
  • 2
  • 13
  • 34
  • How about this [Dynamic event handlers for Xaml-only](https://denisvuyka.wordpress.com/2010/02/21/dynamic-event-handlers-for-xaml-only-silverlight-content-xamlreader-ramora-pattern-and-mef/) – Artavazd Balayan May 31 '16 at 10:05
  • 2
    Looks like `XamlReader.Parse` does not support event handlers. If it did, it would at least need some context to be able to resolve event handler methods. Is there a particular reason why you need to parse XAML at run-time, instead of using data templates or just toggling the visibility of UI elements? – Pieter Witvoet May 31 '16 at 10:07
  • Dear Pieter , my Application UI is dynamic, it is not fixed. – Anson Tan May 31 '16 at 10:10
  • Possible duplicate of [Using Events/Commands with XamlReader](http://stackoverflow.com/questions/3532287/using-events-commands-with-xamlreader) – decPL May 31 '16 at 10:11
  • @AnsonTan: parsing XAML at run-time is not the only way to achieve a dynamic UI. What sort of changes do you want to make to your UI at run-time? Showing and hiding a few context-sensitive buttons? – Pieter Witvoet May 31 '16 at 10:41
  • 1
    If you want to create your UI "dynamically", just use code to create your objects directly. Don't construct a XAML string for it. That's completely defeating the point here and is vastly prone to error. – ManoDestra May 31 '16 at 13:30

1 Answers1

3

Since the error message states that to specify events, you need to compile your XAML file, and you specifically do not want to do that, it seems to be impractical (you may be able to compile a temporary assembly and load it, but then you can't unload it again).

What you could do is give the element a name:

xaml = xaml + "<Button x:Name='ClickMe'>";

And then, using a helper function to retrieve it:

var button = UIHelper.FindChild<Button>(element, "ClickMe");
button.Click += btnMy_Click;

The helper function looks like this, I took it from How can I find WPF controls by name or type? :

/// <summary>
/// Finds a Child of a given item in the visual tree. 
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter. 
/// If not matching item can be found, 
/// a null parent is being returned.</returns>
public static T FindChild<T>(DependencyObject parent, string childName)
   where T : DependencyObject
{    
  // Confirm parent and childName are valid. 
  if (parent == null) return null;

  T foundChild = null;

  int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
  for (int i = 0; i < childrenCount; i++)
  {
    var child = VisualTreeHelper.GetChild(parent, i);
    // If the child is not of the request child type child
    T childType = child as T;
    if (childType == null)
    {
      // recursively drill down the tree
      foundChild = FindChild<T>(child, childName);

      // If the child is found, break so we do not overwrite the found child. 
      if (foundChild != null) break;
    }
    else if (!string.IsNullOrEmpty(childName))
    {
      var frameworkElement = child as FrameworkElement;
      // If the child's name is set for search
      if (frameworkElement != null && frameworkElement.Name == childName)
      {
        // if the child's name is of the request name
        foundChild = (T)child;
        break;
      }
    }
    else
    {
      // child element found.
      foundChild = (T)child;
      break;
    }
  }

  return foundChild;
}
Community
  • 1
  • 1
Manuel Schweigert
  • 4,884
  • 4
  • 20
  • 32