在Silverlight中使用字符串作为数据模板时的事件处理程序

本文关键字:程序 事件处理 数据 Silverlight 字符串 | 更新日期: 2023-09-27 17:50:48

我正在尝试使用字符串以编程方式为数据形式形成一些xaml。我可以让组合框出现。但当我试图使用代码指定"MouseLeftButtonUp"或"加载"的事件处理程序在字符串;进入页面后,页面会变成白色(没有明显的错误)。请参阅下面的相关代码。

     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;

在Silverlight中使用字符串作为数据模板时的事件处理程序

在XAML中连接的事件处理程序需要在连接到XAML文件的后台代码中声明。在ResourceDictionary或从XamlReader加载的任何东西的情况下。加载时不能有任何代码滞后,因此不能在XAML中设置事件处理程序。绕过此限制的最简单方法是不使用字符串构建模板,而只是在XAML文件的参考资料部分声明它,然后可以这样做:

Resources["MyTemplate"] as DataTemplate

来获取模板并在代码中分配它,就像你在这里做的那样,或者只是在XAML中使用StaticResource。只要它保持在连接到此代码的相同XAML文件中,当前其中的事件处理程序应该可以正常工作。字符串的动态部分也需要更改以使用Bindings。

如果您想坚持使用XamlReader方法,那么有两个问题需要解决。

  1. 在渲染模板中找到ComboBox实例
  2. 等待模板渲染完成后寻找ComboBox

要找到ComboBox,您需要首先在模板文本中给它一个x:Name属性(您可以替换当前存在的事件代码)。接下来,您需要能够根据名称在可视化树中定位项目。这是相当简单的,你可以在这里找到一个例子。

要在正确的时间调用这段代码,你要么需要重写OnApplyTemplate,不幸的是,如果你在UserControl之类的东西中,这将不起作用,或者使用另一个技巧来阻止它运行,直到所有控件都呈现出来。下面是一个完整的例子,可以放在构造函数中,并使用上面链接的find方法:

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

在您的情况下,看起来您的模板可能需要等待切换到编辑状态,然后才能呈现,在这种情况下,您需要推迟连接事件,并在您的数据表单上查找当该状态更改时发生的其他事件

一种解决方案是处理DataForm的BeginningEdit事件,并使用它来订阅ComboBox的MouseLeftButtonUp事件。

要做到这一点,在代码后面添加一个名为isEventWiredUp的私有字段。我们将使用此字段来跟踪我们是否订阅了该事件,并防止该事件被多次订阅。

接下来,将x:Name="..."属性添加到ComboBox。我们使用这个名称来访问组合框。

完成后,添加以下两个方法,应该执行您想要的操作。将yourComboBoxName替换为您给组合框的x:Name:

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

为DataForm的BeginningEdit事件订阅这两个方法中的第一个。

我不得不承认我无法让MouseLeftButtonUp事件在ComboBox上触发。我不确定为什么会发生这种情况,但这似乎是ComboBox的一般问题,而不是由于构造XAML的方式而发生的问题。但是,我能够为ComboBox的SelectionChanged事件获得一个事件处理程序。

我还尝试用直接调用OnBeginEdit方法来替换Dispatcher.BeginInvoke行,但我发现这种方法不起作用。这些事件并没有正确地连接起来;我也不知道为什么。

您可以使用交互性来绑定事件,而不是尝试直接连接事件

...
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>");

,您可能必须使用一些祖先绑定来获得定义处理程序的上下文。我使用了InvokeCommandAction的自定义实现;基本上是system . windows . interactive . invokecommandaction的副本,但进行了扩展,以便将事件参数传递给命令,您可能也想这样做。

XamlReader。加载不允许在其中附加eventhandler。因此,使用这种技术来动态地将eventhandler附加到它上面。

1-编写不带eventhandler的Xaml字符串-但要编写这些控件的Name属性。

2-用XamlReader.Load(str);

加载字符串

3-然后加载DataTemplate的内容。using Grid template = ((Grid)(dt.LoadContent()));

注意:这里GridDataTemplate的父控件。

4-按名称找到要附加事件处理程序的控件。Button img = (Button)template.FindName("MyButtonInDataTemplate");