动态创建具有样式的按钮:指定的元素已经是另一个元素的逻辑子元素。首先断开它

时间:2012-03-15 14:48:36

标签: c# wpf wpf-controls

我的应用程序可以导航到3个不同的页面,这些页面都在一个窗口中显示(一次一个)。在同一窗口中显示的所有页面都是用户控件。

每当我点击“A”按钮时,都会创建一个新按钮并添加到 stackpanel 。所以基本上如果我按下5次,我会将5个按钮放在彼此相邻的位置。 下面的代码将屏幕从页面B导航回A(假设我已经在页面B上),这里我调用一个方法,通过已保存的按钮列表进行迭代。

为了简化我希望保存在列表或其他内容中创建的项目,所以当再次加载此窗口时,我可以简单地加载值(按钮)。 < / em>的

Page B:

private void btnNoProduction_Click(object sender, RoutedEventArgs e)
{
    MainWindow main = new MainWindow();

    Switcher.Switch(main);       
    main.reloadDynamicRegistrationContent();
}

页面A(主窗口),我用保存的按钮迭代列表。

public void reloadDynamicRegistrationContent()
{
    for (int i = 0; i < GlobalVar._listReg.Count; i++)
    {
        spHorizontal.Children.Add(GlobalVar._listReg[i]);  //ERROR Specified Element is already the logical.....
    }
}

我需要某种方式来保存这些按钮(列表,但我不断收到错误指定的元素已经是另一个元素的逻辑子元素。首先断开它.wpf 。 当我尝试从静态类访问列表时失败。

动态创建按钮的代码:

private void btnTestRegistration_Click(object sender, RoutedEventArgs e)
{
    Button newBtn = new Button();
    newBtn.Name = "btnRegistration" + registrationCount.ToString();
    newBtn.Width = 151;
    newBtn.Height = 73;
    newBtn.Click += new RoutedEventHandler(this.newBtn_Click);

    Thickness margin = newBtn.Margin;
    margin.Left = 10;
    margin.Right = 5;
    newBtn.Margin = margin;

    Style style = this.FindResource("ButtonStyleRegistration") as Style;
    newBtn.Style = style;

    //force build template
    newBtn.ApplyTemplate();

    var tb = VisualTreeNavigator.FindVisualChild<TextBlock>(newBtn, "tbRegistration1");
    tb.Text = "text cause 1 " + registrationCount.ToString();

    var tb2 = VisualTreeNavigator.FindVisualChild<TextBlock>(newBtn, "tbRegistration2");
    tb2.Text = "text cause 2 " + registrationCount.ToString();

    spHorizontal.Children.Add(newBtn);
    GlobalVar.AddButtonToList(newBtn); 
    registrationCount++;
}

到目前为止,所有内容都已正确添加但我无法保存每当我希望稍后返回此屏幕时创建的一组按钮。

GlobalVar是一个静态类,我有一个列表。在这里,我尝试保存列表中创建的所有按钮的内容。

 public class GlobalVar
{
    public static List<Button> _listReg = new List<Button>();

    public static void AddButtonToList(Button b)
    {
        _listReg.Add(b);
    }
}

1 个答案:

答案 0 :(得分:1)

在WPF中,每个UI元素只能在任何逻辑树中参与一次。你不能有&lt; Button&gt;元素作为子元素(子属性,子元素,内容属性等)不止一次。

似乎MainWindow尚未被处置。即使你正在处理它(即MainWindow超出范围,你关闭它) - 我不确定你从你的代码做什么 - 它不一定处理,因为垃圾收集不一定处理元素immediatley 。按钮仍然可以引用它们的父窗口(spHorizo​​ntal,最终一直到MainWindow),并且由于GlobalVar保持活动状态 - MainWindow根本就没有被处理掉,而你实际上有泄漏。

如果您根本没有解雇主窗口,只是试图让MainWindow的多个副本同时运行,那么您要做的就是不可能。你必须以某种方式序列化按钮列表(基本上,你放在文本框上的文本),然后创建具有相同文本的新按钮。

如果您要关闭MainWindow,那么在关闭Window之前,请确保使用 spHorizo​​ntal.Children.Remove

从spHorizo​​ntal中删除按钮列表。

说了这么多,你的程序有很多改进的机会。具体来说 - 为什么不隐藏主窗口(this.IsVisible = false),然后在必要时再次显示它?或者,为什么不在您的GlobalVar单例中存储spHorizo​​ntal(在从树中删除它之后)。

此外,在应用于按钮的样式的btnTestRegistration_Click方法部分中设置所有属性会更好。我不是一个反码背后的狂热分子,但这显然是你应该使用XAML的地方。

哎呀,如果这是我的代码,我会有一个带项目列表的ObservableCollection(每个项目代表一个按钮),然后是&lt; ItemsControl&gt;绑定到集合,并将ItemsPanelTemplate设置为StackPanel。

<ItemsControl ItemsSource="...">
  <!-- Items Source above should point to the list of logical items representing the buttons. -->
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <!-- this is the spHorizontal that you have -->
      <StackPanel Orientation="Vertical" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <!-- this is a template for the button: -->
      <Button ... />
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>