私はMVVMを学び始めたばかりで、ここに基本的な質問のように思われるものがありますが、それを理解するために丸一日を費やしました。
モデル用、ViewModel用、View用の3つのプロジェクトを含むソリューションがあります。モデルには、TextとCheckStatusの2つのプロパティを持つクラスが含まれています。
ViewModelには、3つのアイテムを持つlistOfItemsというリストがあり、各アイテムにはモデルからのこれらの2つのプロパティがあります。
ビューにはlistViewがあり、その中にCheckBoxがあります。 CheckBoxコンテンツをプロパティTextにバインドする適切な方法は何ですか?
こちらがモデルです
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TheModel
{
public class CheckBoxListModel : INotifyPropertyChanged
{
private string text;
public string Text
{
get { return text; }
set
{
text = value;
RaiseChanged("Text");
}
}
private bool checkStatus;
public bool CheckStatus
{
get { return checkStatus; }
set
{
checkStatus = value;
RaiseChanged("CheckStatus");
}
}
private void RaiseChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
こちらがビューモデルです
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using TheModel;
namespace TheViewModel
{
public class TheViewModel
{
public List<CheckBoxListModel> ListOfItems { get; set; }
public TheViewModelClass()
{
ListOfItems = new List<CheckBoxListModel>
{
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 1",
},
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 2",
},
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 3",
}
};
}
public static implicit operator List<object>(TheViewModelClass v)
{
throw new NotImplementedException();
}
}
}
そして、これがView XAMLです
<UserControl
xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:TheView.Managers" xmlns:TheViewModel="clr-
namespace:TheViewModel;Assembly=TheViewModel"
x:Class="TheView.Styles.ListViewDatabaseStyle">
<UserControl.DataContext>
<TheViewModel:TheViewModelClass/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Button Content="Continue" Style="{StaticResource ButtonStyle}"
Margin="1104,27,40,40"/>
<ListView x:Name="listView1" SelectionMode="Multiple"
Style="{StaticResource ListViewStyle}" Margin="10,55,10,10"
ctrl:ListViewLayoutManager.Enabled="true" ItemsSource="
{Binding TheViewModelClass}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Competency Items"
ctrl:ProportionalColumn.Width="1100"/>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle >
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding
CheckedStatus}"/>
<Setter Property="HorizontalContentAlignment"
Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox
Click="CheckBox_Click"
Content="{Binding Path=TheViewModelClass.Text}"
IsChecked="{Binding
Path=TheViewModelClass.CheckedStatus}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
ここにコードの背後にあるビューがあります。ここに何かはないはずですが、その部分はどこに行くべきですか?
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System;
using System.Text;
using TheViewModel;
namespace TheView.Styles
{
public partial class ListViewDatabaseStyle : UserControl
{
public ListViewDatabaseStyle()
{
InitializeComponent();
}
public List<string> selectedNames = new List<string>();
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var ChkBox = sender as CheckBox;
var item = ChkBox.Content;
bool isChecked = ChkBox.IsChecked.HasValue ? ChkBox.IsChecked.Value
: false;
if (isChecked)
selectedNames.Add(item.ToString());
else
selectedNames.Remove(item.ToString());
}
}
}
まず第一に。プロジェクトの依存関係を設定します。 ViewModelにはアクセスモデルが必要です。 (ViewおよびModelプロジェクトは、他のプロジェクトを参照する必要はありません。)私があなたなら、StartUpプロジェクトを作成して、コントロールをViewModelに転送します。この「スタートアップ」プロジェクトはWPFで、その他はすべて「クラスライブラリ」である必要がありますが、プロジェクトに必要な参照を追加することを忘れないでください(たとえば、ユーザーコントロールを作成するビュープロジェクトのsystem.xaml)。
プロジェクトの依存関係:-StartUp-> ViewModel; (-ViewModel-> View;またはDIでこれを避ける)-ViewModel-> Model; (インターフェイス用に別のプロジェクトを作成する必要がありますが、これは単なる私の倒錯です。)
StartUp Project:これで、スタートアップ(WPF)プロジェクトには(app.xaml.cs)が含まれます:
protected override void OnStartup(StartupEventArgs e)
{
// delete the startupuri tag from your app.xaml
base.OnStartup(e);
//this MainViewModel from your ViewModel project
MainWindow = new MainWindow(new MainViewModel());
}
スタートアップwpfプロジェクトで唯一のもの(ウィンドウ)(ユーザーコントロールを表示するため)。
MainWindow.xamlコンテンツ:
<Window x:Class="StartUp.MainWindow"
xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
Title="MainWindow" WindowState="Maximized" WindowStyle="None" AllowsTransparency="True">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding Control}"/>
</Window>
(およびxaml.cs)
public partial class MainWindow : Window
{
public MainWindow(INotifyPropertyChanged ViewModel)
{
InitializeComponent();
this.DataContext = ViewModel;
this.Show();
}
}
そして、これがすべてのStartUp WPFプロジェクトです。このようにして、ViewModelプロジェクトにコントロールを渡しました。
(さて、それは単なる追加ですが、ユーザーコントロールを処理するために "ViewService"を作成する必要があります)
すべてのビューを検索し、ビューをViewModelと一致させるためのインターフェース。
public interface IControlView
{
INotifyPropertyChanged ViewModel { get; set; }
}
ビューを保存してビューモデルと一致させるシングルトンを作成しました。 (この部分はスキップできます。)これをModelプロジェクトで定義しました。
public class ViewService<T> where T : IControlView
{
private readonly List<WeakReference> cache;
public delegate void ShowDelegate(T ResultView);
public event ShowDelegate Show;
public void ShowControl<Z>(INotifyPropertyChanged ViewModel)
{
if (Show != null)
Show(GetView<Z>(ViewModel));
}
#region Singleton
private static ViewService<T> instance;
public static ViewService<T> GetContainer
{
get
{
if (instance == null)
{
instance = new ViewService<T>();
}
return instance;
}
}
private ViewService()
{
cache = new List<WeakReference>();
var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(r => typeof(T).IsAssignableFrom(r) && !r.IsInterface && !r.IsAbstract && !r.IsEnum);
foreach (Type type in types)
{
cache.Add(new WeakReference((T)Activator.CreateInstance(type)));
}
}
#endregion
private T GetView<Z>(INotifyPropertyChanged ViewModel)
{
T target = default(T);
foreach (var wRef in cache)
{
if (wRef.IsAlive && wRef.Target.GetType().IsEquivalentTo(typeof(Z)))
{
target = (T)wRef.Target;
break;
}
}
if(target==null)
target = (T)Activator.CreateInstance(typeof(Z));
if(ViewModel != null)
target.ViewModel = ViewModel;
return target;
}
}
そして、あなたはViewModelからメインウィンドウにUserControlsを表示する「サービス」を手に入れました。
public class MainViewModel : INotifyPropertyChanged
{
private IControlView _control;
public IControlView Control
{
get
{
return _control;
}
set
{
_control = value;
OnPropertyChanged();
}
}
public MainViewModel()
{ //Subscribe for the ViewService event:
ViewService<IControlView>.GetContainer.Show += ShowControl;
// in this way, here is how to set a user control to the window.
ViewService<IControlView>.GetContainer.ShowControl<ListViewDatabaseStyle>(new TheViewModel(yourDependencyItems));
//you can call this anywhere in your viewmodel project. For example inside a command too.
}
public void ShowControl(IControlView ControlView)
{
Control = ControlView;
}
//implement INotifyPropertyChanged...
protected void OnPropertyChanged([CallerMemberName] string name = "propertyName")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
この「ViewService」を使用したくない場合。 UserControlインスタンスを作成し、ViewのDataContextをViewModelと一致させ、このビューをControlプロパティに渡します。これがリスト付きのViewModelです(まだViewMoldelプロジェクトにあります)。
public class TheViewModel
{
private readonly ObservableCollection<ISelectable> listOfItems;
public ObservableCollection<ISelectable> ListOfItems
{
get { return listOfItems; }
}
public ICommand SaveCheckedItemsText{
get{ return new RelayCommand(CollectNamesOfSelectedElements);}
}
public IEnumerable<ISelectable> GetSelectedElements
{
get { return listOfItems.Where(item=>item.CheckStatus); }
}
public TheViewModel(IList<ISelectable> dependencyItems)
{
listOfItems= new ObservableCollection<ISelectable>(dependencyItems);
}
//here is your list...
private List<string> selectedNames
//use this...
private void CollectNamesOfSelectedElements()
{
selectedNames = new List<string>();
foreach(ISelectable item in GetSelectedElements)
{
//you should override the ToString in your model if you want to do this...
selectedNames.Add(item.ToString());
}
}
}
表示:(ここにすべてのユーザーコントロールを保持します。)
UserControl(xaml)で:
<UserControl x:Class="View.ListViewDataStyle"
xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.Microsoft.com/expression/blend/2008" namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"
mc:Ignorable="d">
<Button Command={Binding SaveCheckedItemsText}/>
<!-- Another content -->
<ListView ItemsSource="{Binding ListOfItems}">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" IsChecked="{Binding CheckedStatus}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
そして、ここにインターフェースがあるのはxaml.csコードです(UserControls用):
public partial class ListViewDatabaseStyle : UserControl, IControlView
{
public ListViewDatabaseStyle ()
{
InitializeComponent();
}
public INotifyPropertyChanged ViewModel
{
get
{
return (INotifyPropertyChanged)DataContext;
}
set
{
DataContext = value;
}
}
}
最後の1つは、モデルを含むModelプロジェクトです。
public interface ISelectable
{
bool CheckStatus { get; set; }
}
public class CheckBoxListModel : INotifyPropertyChanged, ISelectable
{
private string text;
public string Text
{
get { return text; }
set
{
text = value;
RaiseChanged("Text");
}
}
private bool checkStatus;
public bool CheckStatus
{
get { return checkStatus; }
set
{
checkStatus = value;
RaiseChanged("CheckStatus");
}
}
private void RaiseChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
英語の文法の間違いについてすみません、私の投稿を理解していただければ幸いです。
更新:DI技術を使用します。ビューモデルからのビューへの参照を避けるため。 DIサービスは、コンストラクター注入を使用して正しいオブジェクトを注入します。
<UserControl.DataContext>
<TheViewModel:TheViewModelClass/>
</UserControl.DataContext>
<ListView ItemsSource="{Binding ListOfItems}">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" IsChecked="{Binding CheckedStatus}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>