7

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

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

4 回答 4

1

一种解决方案是处理BeginningEditDataForm 的事件并使用它为您的事件处理程序订阅 ComboBox 的MouseLeftButtonUp事件。

为此,请在您的代码隐藏中添加一个名为isEventWiredUp. 我们将使用此字段来跟踪我们是否已订阅该事件并防止该事件被多次订阅。

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

完成后,添加以下两个方法,它们应该可以满足您的需求。替换yourComboBoxNamex: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事件获得一个事件处理程序来工作。

我也尝试用Dispatcher.BeginInvoke直接调用该OnBeginEdit方法来替换该行,但我发现这种方法不起作用。事件没有完全正确连接。再说一次,我不知道为什么。

于 2013-01-17T21:23:41.950 回答
1

连接到 XAML 中的事件处理程序需要在连接到 XAML 文件的代码隐藏中声明。对于 ResourceDictionary 或从 XamlReader.Load 加载的任何内容,不能有任何代码隐藏,因此不能在 XAML 中设置事件处理程序。绕过此限制的最简单方法是不从字符串构建模板,只需在 XAML 文件的 Resources 部分中声明它,然后您可以这样做:

Resources["MyTemplate"] as DataTemplate

获取模板并像您在这里所做的那样在代码中分配它,或者只是在 XAML 中使用 StaticResource。只要它保留在连接到此代码的同一个 XAML 文件中,您当前拥有的事件处理程序应该可以正常工作。字符串的动态部分也需要更改以使用绑定。

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

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

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

于 2013-01-17T21:30:06.590 回答
0

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

例如

...
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.Interactivity.InvokeCommandAction 的副本,但经过扩展,以便将事件参数传递给命令,您可能也想做同样的事情。

于 2013-01-22T12:03:13.593 回答
0

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.

于 2015-05-01T09:40:53.077 回答