将不同的对象添加到Groupbox中

时间:2014-01-02 14:51:25

标签: c# wpf exception

我有2个按钮'添加标题'和'添加问题'。他们将单击“添加标题”,然后ComboBox和TextBox将显示如下:

enter image description here 从方法中可以看出,这些对象存储在GroupBox中:

C#代码:

private void btnAddTitle_Click(object sender, RoutedEventArgs e)
{

        CurrentSortItem++;
        SortItems.Add(CurrentSortItem);

        outerSp =  new StackPanel() { Orientation = Orientation.Vertical };
        sp = new StackPanel() { Orientation = Orientation.Horizontal };
        gp = new GroupBox();

        ComboBox y = new ComboBox();
        y.Name = "Combo" + CurrentSortItem;
        y.SelectedItem = CurrentSortItem;
        y.Height = 25;
        y.Width = 45;
        y.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
        y.Margin = new Thickness(20, 15, 0, 0);

        foreach (int item in SortItems)
        {
            y.Items.Add(item);
        }

        TextBox x = new TextBox();
        x.Name = "Title" + CurrentSortItem;
        x.Text = "Title...";
        x.FontWeight = FontWeights.Bold;
        x.FontStyle = FontStyles.Italic;
        x.TextWrapping = TextWrapping.Wrap;
        x.Height = 25;
        x.Width = 200;
        x.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
        x.Margin = new Thickness(12, 15, 0, 0);

        sp.Children.Add(y);
        sp.Children.Add(x);

        outerSp.Children.Add(sp);
        gp.Content = outerSp;

        spStandard.Children.Add(gp);

}

然后,当用户点击“添加问题”时,我需要将对象(ComboBox和TextBox)添加到同一GroupBox中的标题下。

这是添加问题的方法:

C#代码:

private void ViewQuestions(StackPanel sp)
{

        var stackPanel = gp.Content as StackPanel;
        if (stackPanel != null)
        {
            stackPanel.Children.Add(sp);
        }
        else
            gp.Content = sp;
}

    List<int> SortItems1 = new List<int>();
    int CurrentSortItem1 = 0;
    int Count = 0;

private void btnQuestion_Click(object sender, RoutedEventArgs e)
{
        outerSp = new StackPanel() { Orientation = Orientation.Vertical };
        sp = new StackPanel() { Orientation = Orientation.Horizontal };

        if (SortItems.Count == 0)
        {
            MessageBox.Show("You must add a title before adding a question", "ERROR", MessageBoxButton.OK, MessageBoxImage.Information);
        }
        else
        {
            Count++;

            CurrentSortItem1++;
            SortItems1.Add(CurrentSortItem1);

                ComboBox y = new ComboBox();
                y.Name = "Combo" + CurrentSortItem1;
                y.SelectedItem = CurrentSortItem1;
                y.Height = 25;
                y.Width = 45;
                y.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                y.Margin = new Thickness(20, 15, 0, 0);

                foreach (int item in SortItems1)
                {
                    y.Items.Add(item);
                }

                TextBox x = new TextBox();
                x.Name = "Question" + CurrentSortItem1;
                x.Text = "Question...";
                x.FontStyle = FontStyles.Italic;
                x.TextWrapping = TextWrapping.Wrap;
                x.Height = 25;
                x.Width = 500;
                x.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                x.AcceptsReturn = true;
                x.Margin = new Thickness(100, 15, 0, 0);

                TextBox z = new TextBox();
                z.Name = "Points" + CurrentSortItem;
                z.FontWeight = FontWeights.Bold;
                z.Height = 25;
                z.Width = 45;
                z.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                z.Margin = new Thickness(250, 15, 0, 0);

                sp.Children.Add(y);
                sp.Children.Add(x);
                sp.Children.Add(z);

                outerSp.Children.Add(sp);

                ViewQuestions(sp);

    }

这是我试图让问题对象出现在与标题相同的GroupBox中。此代码返回错误:

enter image description here

编辑:

这就是我想要实现的目标。

enter image description here

对不起,如果说得不够。

1 个答案:

答案 0 :(得分:3)

HighCoreSean所说的是Templating。模板化允许将UI实现与数据分离。在WPF中,这种关注点分离通常通过 MVVM 模式实现,其中( M )odel包装/公开数据实体,( V )iew在屏幕上渲染模型,( V )iew( M )odel操纵模型。

因此,作为示例并使用MVVM模式,我创建了两个名为MyTitleModel和MyQuestionModel的模型。我还创建了一个它们都实现的接口,因此我可以将它们存储在ViewModel的同一个集合中。

public interface IModel
{
    string Text { get; set; }
}

public class MyQuestionModel : IModel
{
    public MyTitleModel Title { get; set; }
    public string Field1 { get; set; }
    public string Text { get; set; }
}

public class MyTitleModel : IModel
{
    public string Field2 { get; set; }
    public string Text { get; set; }
}

ViewModel操纵模型,看起来像

public class MyViewModel
{
    public MyViewModel()
    {
        this.Items = new ObservableCollection<IModel>();
        this.Field1Items = new ObservableCollection<string>() { "1", "2", "3" };
        AddTitleCommand = new RelayCommand(o => true, o => Items.Add(new MyTitleModel()));
        AddQuestionCommand = new RelayCommand(o => Items.Any(), o =>
        {
            var title = this.Items.OfType<MyTitleModel>().LastOrDefault();
            Items.Add(new MyQuestionModel() { Title = title });
        });
    }

    public ObservableCollection<IModel> Items { get; set; }

    public ObservableCollection<string> Field1Items { get; set; }

    public RelayCommand AddTitleCommand { get; set; }
    public RelayCommand AddQuestionCommand { get; set; }
}

这是一个简单的视图模型。最复杂的部分是RelayCommand,它允许我直接从View中调用ViewModel。

然后视图将其DataContext设置为ViewModel,并使用模板在项目控件中显示模型。

<Window x:Class="StackOverflow._20885502.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:this="clr-namespace:StackOverflow._20885502"
        DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate DataType="{x:Type this:MyTitleModel}">
            <StackPanel Orientation="Horizontal">
                <ComboBox SelectedValue="{Binding Path=Field2}" ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type this:MainWindow}}, Path=ViewModel.Field1Items}" Margin="1 2" />
                <TextBox Text="{Binding Path=Text}" Margin="1 2" Width="200" />
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type this:MyQuestionModel}">
            <StackPanel Orientation="Horizontal">
                <ComboBox SelectedValue="{Binding Path=Field1}" ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type this:MainWindow}}, Path=ViewModel.Field1Items}" Margin="1 2" />
                <TextBox Text="{Binding Path=Text}" Margin="20 2 1 2" Width="200" />
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <DockPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
            <Button Content="Add Title" Command="{Binding Path=AddTitleCommand}" Width="100" Margin="3" />
            <Button Content="Add Question" Command="{Binding Path=AddQuestionCommand}" Width="100" Margin="3" />
        </StackPanel>
        <ItemsControl ItemsSource="{Binding Path=Items}" />
    </DockPanel>
</Window>

注意Xaml中DataContext的设置。我可以在后面的代码中设置这个,因为很多例子都可以,但是在Xaml中设置DataContext,visual studio将在简单的数据绑定路径上自动完成。

另请注意2个DataTemplates。我没有设置ItemsControl的ItemTemplate。通过执行此操作,ItemsControl将搜索具有正确DataType的未键控DataTemplate以显示每个项目。

后面的代码只是创建并存储对ViewModel的引用。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        ViewModel = new MyViewModel();
        InitializeComponent();
    }

    public MyViewModel ViewModel { get; set; }
}

该按钮连接到RelayCommand,因此ViewModel可以将MyModel对象添加到Items集合。

这是我的测试解决方案的完整代码,不包括您可以从此question的答案中获得的RelayCommand,因此您应该能够轻松地重现它。

修改

我为此示例实现的简单中继命令是

public class RelayCommand: ICommand
{
    private Predicate<object> canExecute;

    private Action<object> execute;

    public RelayCommand(Predicate<object> canExecute, Action<object> execute)
    {
        this.canExecute = canExecute;
        this.execute = execute;
    }

    public void Execute(object parameter)
    {
        this.execute.Invoke(parameter);
    }

    public bool CanExecute(object parameter)
    {
        return this.canExecute.Invoke(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

我希望这会有所帮助。