我有2个按钮'添加标题'和'添加问题'。他们将单击“添加标题”,然后ComboBox和TextBox将显示如下:
从方法中可以看出,这些对象存储在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中。此代码返回错误:
编辑:
这就是我想要实现的目标。
对不起,如果说得不够。
答案 0 :(得分:3)
HighCore和Sean所说的是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; }
}
}
我希望这会有所帮助。