itemsSourceを使用してWPFリストボックスを入力します - 良いアイデア?
-
25-10-2019 - |
質問
私は(比較的)経験豊富なココア/Objective-Cコーダーであり、自分自身をC#とWPFフレームワークを教えています。
cocoaで、andを埋めるとき NSTableView
, 、それは比較的単に、ビューにデリゲートとデータソースを割り当てることです。次に、これらの委任/DataSourceメソッドを使用して、テーブルに入力し、その動作を決定します。
オブジェクトのリストがある簡単なアプリケーションをまとめています。 Dog
オブジェクト、それぞれが持っている public string name
. 。これはの返品値です Dog.ToString()
.
オブジェクトはaに表示されます ListBox
, 、そして私はココアの同様のパターンを使用してこのビューを入力したいと思います NSTableViewDataSource
. 。現在、以下を使用して動作しているようです。
public partial class MainWindow : Window, IEnumerable<Dog>
{
public Pound pound = new Pound();
public MainWindow()
{
InitializeComponent();
Dog fido = new Dog();
fido.name = "Fido";
pound.AddDog(fido);
listBox1.ItemsSource = this;
Dog spot = new Dog();
spot.name = "Spot";
pound.AddDog(spot);
}
public IEnumerator<Dog> GetEnumerator()
{
return currentContext.subjects.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
しかし、私はどのように疑問に思っています 正しい これは。私は文字通り、ビジュアルスタジオを1時間未満でインストールしていたので、自分が何をしているのかわからないと言っても安全です。
- これは適切なパターンですか?
- 2番目のアイテムをリストに追加します(
spot
)を更新しているようですListBox
適切に、しかし、私は何がアップデートをトリガーしているのだろうと思っていますか? - 更新するとどうなりますか
Pound
背景スレッドで? - どうすれば手動で尋ねることができますか
ListBox
自分自身を更新するには? (私もする必要がありますか?)
1つの変更 私が作る必要があることを知っていることは、 IEnumerable<Dog>
同様に、独自のクラスへの実装 DogListItemsSource
, 、しかし、私はそれを研磨する前に確実なアプローチを持っていることを確認したいです。
お気軽に コメントでは、大小を問わず、他のポイントに対処または念頭に置いておく必要があります。これを正しい方法で、初めて学びたいと思います。
解決
私の提案は、あなたにデータを提供する責任があるウィンドウ以外にクラスを作成することです ListBox
. 。一般的なアプローチはWPFと呼ばれます MVVM, 、どのパターンと同様に多くの実装があります。
基本は各モデルです(例: Pound
と Dog
)UIから簡単にやり取りできる方法でモデルを提示する責任のあるビューモデルがあります。
開始するために、WPFは優れたクラスを提供します。 ObservableCollection<T>
, 、これは、誰かが追加、移動、または削除されるたびに、「Hey I Chander」イベントを解雇するコレクションです。
以下は、MVVMを教えるつもりはなく、MVVMのフレームワークを使用しない例です。ただし、いくつかのブレークポイントを設定してプレイすると、バインディング、コマンド、InotifyPropertyChanged、およびObservableCollectionについて学びます。これらはすべて、WPFアプリケーション開発で大きな役割を果たしています。
から始まります MainWindow
, 、あなたはあなたを設定することができます DataContext
ビューモデルへ:
public class MainWindow : Window
{
// ...
public MainWindow()
{
// Assigning to the DataContext is important
// as all of the UIElement bindings inside the UI
// will be a part of this hierarchy
this.DataContext = new PoundViewModel();
this.InitializeComponent();
}
}
どこ PoundViewModel
のコレクションを管理します DogViewModel
オブジェクト:
public class PoundViewModel
{
// No WPF application is complete without at least 1 ObservableCollection
public ObservableCollection<DogViewModel> Dogs
{
get;
private set;
}
// Commands play a large role in WPF as a means of
// transmitting "actions" from UI elements
public ICommand AddDogCommand
{
get;
private set;
}
public PoundViewModel()
{
this.Dogs = new ObservableCollection<DogViewModel>();
// The Command takes a string parameter which will be provided
// by the UI. The first method is what happens when the command
// is executed. The second method is what is queried to find out
// if the command should be executed
this.AddDogCommand = new DelegateCommand<string>(
name => this.Dogs.Add(new DogViewModel { Name = name }),
name => !String.IsNullOrWhitespace(name)
);
}
}
そしてあなたのxamlで(必ずマッピングしてください xmlns:local
XAMLがビューモデルを使用できるようにします):
<!-- <Window ...
xmlns:local="clr-namespace:YourNameSpace" -->
<!-- Binding the ItemsSource to Dogs, will use the Dogs property
-- On your DataContext, which is currently a PoundViewModel
-->
<ListBox x:Name="listBox1"
ItemsSource="{Binding Dogs}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:DogViewModel}">
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5">
<TextBox Text="{Binding Name}" />
</Border>
</DataTemplate>
</ListBox.Resources>
</ListBox>
<GroupBox Header="New Dog">
<StackPanel>
<Label>Name:</Label>
<TextBox x:Name="NewDog" />
<!-- Commands are another big part of WPF -->
<Button Content="Add"
Command="{Binding AddDogCommand}"
CommandParameter="{Binding Text, ElementName=NewDog}" />
</StackPanel>
</GroupBox>
もちろん、あなたは必要です DogViewModel
:
public class DogViewModel : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return this.name; }
set
{
this.name = value;
// Needed to alert WPF to a change in the data
// which will then update the UI
this.RaisePropertyChanged("Name");
}
}
public event PropertyChangedHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
最後に、の実装が必要です DelegateCommand<T>
:
public class DelegateCommand<T> : ICommand
{
private readonly Action<T> execute;
private readonly Func<T, bool> canExecute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
if (execute == null) throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(T parameter)
{
return this.canExecute != null && this.canExecute(parameter);
}
bool ICommand.CanExecute(object parameter)
{
return this.CanExecute((T)parameter);
}
public void Execute(T parameter)
{
this.execute(parameter);
}
bool ICommand.Execute(object parameter)
{
return this.Execute((T)parameter);
}
}
この答えは、没入型の完全にバインドされたWPF UIを鞭打ちすることは決してありませんが、うまくいけば、UIがコードとどのように対話できるかを感じてください。
他のヒント
WPFでは、通常、アイテムソースとしてコレクションを持っていて、 データテンプレート アイテムを表示します。
通常、それらのコントロールは、itemsourceインスタンスが実装されている場合にのみ更新します
INotifyCollectionChanged
, 、多分あなたはそれの前にアイテムを追加しましたListBox
それを取得しました。ポンドとは?ポンドには、例えばスレッドの関係がある場合を除きます
ObservableCollection
そうする、それは問題ありません、もしあなたが使用する必要があるなら 発送.ListBox.Items.Refresh()
それを行うことができますが、通常、通知付きのコレクションを使用するだけです。
WPFはデータバインディングを重く使用しているので、フレームワークを学びたい場合 それぞれの概要 (一緒に 他のすべて)興味深いかもしれません。