가있다 :
Wrapper.DataContext = new CommonViewModel();
그리고 코드 BindableBase
에 대한 암호.
각 카테고리는 하위 카테고리가 무엇인지 알고 있습니다. 데이터베이스 나 디스크 파일, 데이터베이스/웹 서비스 메서드/파일 판독기/클래스를 반환하면 클래스가 반환되며 일치하도록 뷰 모델을 만들 수 있습니다. 뷰 모델은 정보의 구조를 이해하지만 실제 내용에 대해 아무 것도 모른다. 다른 누군가가 그 일을 담당하고 있습니다.
이것은 모두 매우 선언적입니다. 유일한 루프는 데모 개체를 가짜로 만듭니다. 이벤트 핸들러는 없으며, 뷰 모델을 생성하고 가짜 데이터로 자신을 채우라는 것을 제외하고는 코드 숨김에 아무것도 없습니다. 실생활에서 특별한 경우를 위해 이벤트 핸들러를 작성하는 경우가 종종 있습니다 (예 : 드래그 앤 드롭). 코드 비하인드에 뷰 특정 로직을 배치하는 것에 관한 MVVM 이외의 것은 없습니다. 그게 거기있는거야. 그러나이 경우는 그렇게하기에는 너무 사소한 것입니다. 우리는 정확히 수년간 TFS에 앉아 있던 파일이 마법사가 만든대로 정확히 .xaml.cs
개의 파일을 가지고 있습니다.
viewmodel 속성은 많은 상용구입니다. 나는 #regions와 모든 것들을 생성하기 위해 미리보기 (steal them here)를 가지고있다. 다른 사람들이 복사하여 붙여 넣기.
일반적으로 각 viewmodel 클래스는 별도의 파일에 저장하지만 예제 코드입니다.
C# 6 용으로 작성되었습니다. 이전 버전을 사용하고 계시다면 알려 주시기 바랍니다.
마지막으로, 하나의 콤보 상자 (또는 무엇이든)가 트리를 탐색하는 대신 다른 큰 항목 모음을 필터링한다는 측면에서 생각하는 것이 더 합당한 경우가 있습니다. 특히 "카테고리": "하위 카테고리"관계가 일대 다가 아닌 경우,이 계층 구조 형식으로이를 수행하는 것이 거의 불가능합니다.
이 경우 우리는 "categories"컬렉션과 컬렉션 모두 "하위 범주"를 모두 기본보기 모델의 속성으로 사용하게됩니다. 그런 다음 "카테고리"선택을 사용하여 일반적으로 CollectionViewSource
을 통해 "하위 카테고리"컬렉션을 필터링합니다. 그러나 뷰 모델에 ReadOnlyObservableCollection
(FilteredSubCategories
)과 같은 두 번째 콤보 상자를 바인딩하는 모든 "하위 범주"의 개인 전체 목록을 제공 할 수도 있습니다. '카테고리'선택이 변경되면 SelectedCategory
을 기반으로 FilteredSubCategories
을 다시 채 웁니다.
결론은 데이터의 의미를 반영하는보기 모델을 작성한 다음 사용자가보고해야 할 것을보고 수행해야 할 작업을 수행 할 수 있도록보기를 작성하는 것입니다. 뷰 모델은 뷰가 존재한다는 것을 인식해서는 안됩니다. 그들은 단지 정보와 명령을 폭로합니다. 동일한 뷰 모델을 다른 방법이나 다른 세부 수준으로 표시하는 여러 뷰를 작성할 수 있으므로 뷰 모델은 누구나 사용할 수있는 자체에 대한 정보를 중립적으로 노출하는 것으로 생각하면 편리합니다. 보통 인수 분해 규칙이 적용됩니다 : 커플을 느슨하게 (더 이상 느슨하게하지만) 가능한 한 등
ComboDemoViewModels.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace ComboDemo.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] String propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
#endregion INotifyPropertyChanged
}
public class ComboDemoViewModel : ViewModelBase
{
// In practice this would probably have a public (or maybe protected) setter
// that raised PropertyChanged just like the other properties below.
public ObservableCollection<CategoryViewModel> Categories { get; }
= new ObservableCollection<CategoryViewModel>();
#region SelectedCategory Property
private CategoryViewModel _selectedCategory = default(CategoryViewModel);
public CategoryViewModel SelectedCategory
{
get { return _selectedCategory; }
set
{
if (value != _selectedCategory)
{
_selectedCategory = value;
OnPropertyChanged();
}
}
}
#endregion SelectedCategory Property
public void Populate()
{
#region Fake Data
foreach (var x in Enumerable.Range(0, 5))
{
var ctg = new ViewModels.CategoryViewModel($"Category {x}");
Categories.Add(ctg);
foreach (var y in Enumerable.Range(0, 5))
{
ctg.SubCategories.Add(new ViewModels.SubCategoryViewModel($"Sub-Category {x}/{y}"));
}
}
#endregion Fake Data
}
}
public class CategoryViewModel : ViewModelBase
{
public CategoryViewModel(String name)
{
Name = name;
}
public ObservableCollection<SubCategoryViewModel> SubCategories { get; }
= new ObservableCollection<SubCategoryViewModel>();
#region Name Property
private String _name = default(String);
public String Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged();
}
}
}
#endregion Name Property
// You could put this on the main viewmodel instead if you wanted to, but this way,
// when the user returns to a category, his last selection is still there.
#region SelectedSubCategory Property
private SubCategoryViewModel _selectedSubCategory = default(SubCategoryViewModel);
public SubCategoryViewModel SelectedSubCategory
{
get { return _selectedSubCategory; }
set
{
if (value != _selectedSubCategory)
{
_selectedSubCategory = value;
OnPropertyChanged();
}
}
}
#endregion SelectedSubCategory Property
}
public class SubCategoryViewModel : ViewModelBase
{
public SubCategoryViewModel(String name)
{
Name = name;
}
#region Name Property
private String _name = default(String);
public String Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged();
}
}
}
#endregion Name Property
}
}
MainWindow.xaml에게
<Window
x:Class="ComboDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ComboDemo"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical" Margin="4">
<StackPanel Orientation="Horizontal">
<Label>Categories</Label>
<ComboBox
x:Name="CategorySelector"
ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory}"
DisplayMemberPath="Name"
MinWidth="200"
/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="20,4,4,4">
<Label>Sub-Categories</Label>
<ComboBox
ItemsSource="{Binding SelectedCategory.SubCategories}"
SelectedItem="{Binding SelectedCategory.SelectedSubCategory}"
DisplayMemberPath="Name"
MinWidth="200"
/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs를을
using System.Windows;
namespace ComboDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new ViewModels.ComboDemoViewModel();
vm.Populate();
DataContext = vm;
}
}
}
추가 신용
다음은 MainWindow의 다른 버전입니다.xaml : 두 가지 방법으로 동일한보기 모델을 표시하는 방법을 보여줍니다. 한 목록에서 범주를 선택하면 SelectedCategory
이 업데이트되어 다른 목록에 반영되고 같은 내용이 SelectedCategory.SelectedSubCategory
에 해당합니다.
<Window
x:Class="ComboDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ComboDemo"
xmlns:vm="clr-namespace:ComboDemo.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
>
<Window.Resources>
<DataTemplate x:Key="DataTemplateExample" DataType="{x:Type vm:ComboDemoViewModel}">
<ListBox
ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory}"
>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:CategoryViewModel}">
<StackPanel Orientation="Horizontal" Margin="2">
<Label Width="120" Content="{Binding Name}" />
<ComboBox
ItemsSource="{Binding SubCategories}"
SelectedItem="{Binding SelectedSubCategory}"
DisplayMemberPath="Name"
MinWidth="120"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical" Margin="4">
<StackPanel Orientation="Horizontal">
<Label>Categories</Label>
<ComboBox
x:Name="CategorySelector"
ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory}"
DisplayMemberPath="Name"
MinWidth="200"
/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="20,4,4,4">
<Label>
<TextBlock Text="{Binding SelectedCategory.Name, StringFormat='Sub-Categories in {0}:', FallbackValue='Sub-Categories:'}"/>
</Label>
<ComboBox
ItemsSource="{Binding SelectedCategory.SubCategories}"
SelectedItem="{Binding SelectedCategory.SelectedSubCategory}"
DisplayMemberPath="Name"
MinWidth="200"
/>
</StackPanel>
<GroupBox Header="Another View of the Same Thing" Margin="4">
<!--
Plain {Binding} just passes along the DataContext, so the
Content of this ContentControl will be the main viewmodel.
-->
<ContentControl
ContentTemplate="{StaticResource DataTemplateExample}"
Content="{Binding}"
/>
</GroupBox>
</StackPanel>
</Grid>
</Window>
이 제품을 살펴 보았습니까? http://stackoverflow.com/questions/9185366/mvvm-wpf-master-detail-comboboxes –
나는 그것을 보는 방법, 당신은 모든 일을 잘못하고있다. @SreeHarshaNellore가 준 링크를 참조하십시오. 필요한 것은 단일보기 (두 개의 콤보 상자 포함)와 단일보기 모델입니다. – Jai
나는 각 카테고리에 하위 카테고리 하위 카테고리의 컬렉션을 제공 할 것입니다. x : Category 콤보 CategorySelector의 이름을 지정합니다. 다른 콤보의 ItemsSource = "{Binding SelectedItem.SubCategories, ElementName = CategorySelector}"을 바인딩하십시오. 밥은 당신 삼촌이야. –