2016-06-15 2 views
1

のカスタムユーザー入力を取得する方法を、私は私の問題の簡易版を提示することができますしてください:ベストプラクティス:WPF

は私がして、いくつかのPersonオブジェクトを表示したいと考えているMainWindowと呼ばれるメインウィンドウを持っているとしましょうMainWindow。私はすべての入力を取得しようと

:さて、これらのPersonオブジェクトをインスタンス化するために、私はここで

は私のソリューションです...などなどの名前、年齢、職業、好きな食べ物、など、さまざまな分野の束を必要とします二次ウィンドウに Personをインスタンス化し、その結果をメインフォームに返送します。次のように

MainWindowは、パブリックメソッドを持っています

public void (Person input) 
{ 
    // use the fields in input to add details to window 
} 

私はそのコンストラクタでMainWindowへの参照を取得して、フィールドに保存しますPersonInputと呼ばれるプロジェクトで、別のウィンドウを持っています。

private MainWindow owner; 
    public PersonInput(MainWindow parent) 
    { 
     InitializeComponent(); 
     owner = parent; 
    } 

PersonInputPersonオブジェクトの必要なフィールドに対応する入力フィールドの数を有しています。次のように加えて

は、それが関連するonClickイベントハンドラで「AddPerson」というボタンがあります(擬似コード)

private void button_Click(object sender, RoutedEventArgs e) 
{  
     //get all fields from this form.. 
     String enteredName = this.nameText.Text; 
     //get more fields.... 
     Person p = new Person(...); 

     //owner is MainWindow, send Back the Person so details can be displayed 
     owner.addPerson(p); 
     this.Close(); 
} 

あなたが期待するよう、MainWindowは上の持っている「AddPersonButton」という名前のボタンがありますこのようにイベントハンドラをクリックしてください:このメソッドの動作

private void button_Click(object sender, RoutedEventArgs e) 
{ 
     PersonInput x = new PersonInput(this); //pass this as a reference 
     //so this window can send us back the result when they have it 

     x.Show(); //open the child window so user can enter information 
} 

が、私はそれはそれを行うためのベストプラクティスの方法ではない、かなり確信しています。私はこれを行う慣用的な.net WPFの方法を学びたいと思います。教えてください

+1

私はほとんど2kの担当者を持つユーザーとして、あなたは最高の練習問題はStackOverflowのに適していないことを知っていることを期待していたデータバインディング... –

+0

に関するいくつかのチュートリアルを開始したいです彼らが意見を出した答えを生み出すにつれて。 – DavidG

+0

@DavidGこれは厳密にはベストプラクティスの質問ではありませんが、.NETでこのタスクを達成する方法はわかりませんが、私が試したことを人に知らせるために試してみたいと思っています。 – ForeverStudent

答えて

2

これは私がこのようなことをすることです。 MVVMだけの依存性注入はありません。しかし、それはうまく動作し、the asteroid kills off all the programmers my age、彼らは自分のコードを維持するために雇うエントリレベルの子供はそれに問題がないでしょう。

I は、MVVMを学び、これらのことをviewmodelで行うことを、恐らくと想像できる以上に強く促します。これは絶対にWPFのベストプラクティスです。あなたがWPFを使って作業するとき、あなたの人生は無限に少なくなります。それはよりタイピングとより多くのあなたの頭の前で周りを取得するが、それは本当に報いる。 WPFのすべてがそのように機能するように設計されています。しかし、この特殊な例ではその点を非常にうまく説明していません。

PersonInput.xaml.cs

public class PersonInput : Window 
{ 
    public void PersonInput() 
    { 
     InitializeComponent(); 
     Owner = Application.Current.MainWindow; 
    } 

    // initializer can be null to create a new Person, or you can pass in an 
    // existing Person to edit it. Note that we return an edited *copy*, instead of 
    // editing the original. Thus you can check to see if anything was actually 
    // changed, you can easily support an Undo feature, etc. etc. 
    public static Person ShowDialog(Person initializer) 
    { 
     var vm = new PersonViewModel(initializer); 
     var dlg = new PersonInput() { DataContext = vm }; 

     // ShowDialog() shows dialog *modally*, disabling the parent window 
     // It returns Nullable<bool>. 
     if (dlg.ShowDialog().GetValueOrDefault(false)) 
     { 
      return vm.ToPerson(); 
     } 

     return null; 
    } 

    private void OK_Click(object sender, RoutedEventArgs e) 
    { 
     DialogResult = true; 
    } 

    private void Cancel_Click(object sender, RoutedEventArgs e) 
    { 
     DialogResult = false; 
    } 
} 

PersonInputViewModel.cs

// ViewModelBase defined below 
// This class duplicates Person, but with property-change notifications for the UI. 
// You may see fit to add the changed notifications to Person itself and not bother 
// defining a separate viewmodel. 
// Note also that we can display this ViewModel in many different views, using 
// many different DataTemplates or UserControls. 
// If you end up using it in a lot of places, you might write a Person.ShowDialog() 
// overload that takes a PersonViewModel and edits it, or returns an edited copy. 
public class PersonViewModel : ViewModelBase 
{ 
    public PersonViewModel(Person person = null) 
    { 
     if (person != null) 
     { 
      // Assuming Person has FirstName and LastName properties 
      FirstName = person.FirstName; 
      LastName = person.LastName; 
      // etc. etc. for all the rest 
     } 
    } 

    public Person ToPerson() 
    { 
     return new Person() 
     { 
      FirstName = this.FirstName, 
      LastName = this.LastName, 
      // etc. etc. for all other properties 
     }; 
    } 

    // Any property that will be shown in the dialog should be defined just like this. 
    // This involves a lot of copy and pasting. I wrote a snippet. Here's another: 
    // http://aaron-hoffman.blogspot.com/2010/09/visual-studio-code-snippet-for-notify.html 
    private string _firstName = null; 
    public string FirstName { 
     get { return _firstName; } 
     set { 
      if (value != _firstName) { 
       _firstName = value; 
       // If not in C#6, pass "FirstName" instead. 
       // nameof(FirstName) keeps the parameter correct if you right-click 
       // Rename the property -- that can bite you. 
       OnPropertyChanged(nameof(FirstName)); 
      } 
     } 
    } 
} 

ViewModelBase.cs

public class ViewModelBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    public void OnPropertyChanged(string propertyName) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 

     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

PersonInput.xaml

<Window xmlns:blahblahblah="Blah blah blah" etc etc 
    Title="Person" 
    Height="640" 
    Width="480" 
    ShowInTaskbar="False" 
    ResizeMode="CanResizeWithGrip" 
    WindowStartupLocation="CenterOwner" 
    > 
    <Grid> 
     <Grid.RowDefinitions> 
      <!-- Unused Auto-height rows collapse harmlessly. --> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition MinWidth="180" Width="Auto" /> 
      <ColumnDefinition Width="*" /> 
     </Grid.ColumnDefinitions> 

     <Label Grid.Row="0" Grid.Column="0">First Name</Label> 
     <TextBox 
      Grid.Row="0" 
      Grid.Column="1" 
      Text="{Binding FirstName}" 
      /> 

     <Label Grid.Row="1" Grid.Column="0">Last Name</Label> 
     <TextBox 
      Grid.Row="1" 
      Grid.Column="1" 
      Text="{Binding LastName}" 
      /> 

     <!-- And so on, row by row, for whatever you've got for properties --> 

     <StackPanel 
      Orientation="Horizontal" 
      Grid.Column="1" 
      Grid.Row="10" 
      HorizontalAlignment="Right" 
      > 
      <Button Content="_OK" Click="OK_Click" /> 
      <Button Content="_Cancel" Click="Cancel_Click" /> 
     </StackPanel>     
    </Grid> 
</Window> 

上記のXAMLのGridの一部を別の場所に定義されたDataTemplateと記述し、このテンプレートのContentTemplateプロパティに割り当てられたContentControlに置き換えることができます。その後、Person - 編集XAMLをその場で再利用することができます。 Personsのリストボックスを持っている場合は、コンパクトで読み取り専用のDataTemplateと表示することができます。そのテンプレートは他の場所でも再利用できます。たぶんあなたのリストボックスは、各項目に「編集」チェックボックスを付けることができ、ユーザがそれをチェックすると、その項目のテンプレートを読み込み専用のコンパクトなものから大きなエディタのものに切り替えることができます。

空が限界です! WPFはクールです。

経験豊富なWPFstersは、ここにコマンドを含めていないことに気づくでしょう。コマンドは非常に便利で強力です。それらを使うべきです。しかし、私の見解では、上記は1つの質問に対して十分大きな咬合であり、私はそれらを避けるためにデザインに大きな暴力を起こさなかった。私が書いたビューモデルはPersonDialogViewModelではありません。それはPersonViewModelです。これは、ビューモデルが根本的にどのようなものかをよく表していると思います。 PersonViewModelの属性を持つPersonDialogViewModelは、確かに書いていいことです。

0

このアプローチの最大の弱点は、MainWindowとPersonInputの強力な結合です。

Observer Patternを使用してメインウィンドウを匿名で購読することが、やや良い方法です。

コード簡単な解決策の例:

public interface IAddPersonObserver 
{ 
    void OnPersonAdded(Person person); 
} 

public interface IAddPersonObservable 
{ 
    void Subscribe(IAddPersonObserver observer); 
    void Unsubscribe(IAddPersonObserver observer); 
} 

public class MainWindow : IAddPersonObserver 
{ 
    ... 

    private void button_Click(object sender, RoutedEventArgs e) 
    { 
     PersonInput x = new PersonInput(); 
     x.Subscribe(this); 
     x.Show(); 
    } 

    public void OnPersonAdded(Person addedPerson) 
    { 
     addPerson(addedPerson); // or whatever view update code you want 
    } 
} 

さらなる改善が作成されるか、または示される内容ビュー知ることからメインウィンドウを分離し、そして(例えばPersonRepositoryなど)中間オブジェクトを有する公転なる店舗/ホールド/重要なビジネスデータを提供するこれは、アプリケーションデータを実際にビューおよびアプリケーションウィンドウ内に配置するよりもはるかに優れています。

+0

ベストプラクティスは、IBestPracticeProviderインターフェイスを定義し、IInterfaceObserverInterfaceに対してクエリを実行し、任意のインターフェイス監視インターフェイスとインターフェイスし、InterdepencyAdaptorObjectorInjectorInterfaceファクトリを介して依存関係オブジェクトに依存関係を注入するIInterfaceProviderを問い合わせることです。どちらかというと、新しいPersonインスタンスを返すPersonDialogを書くだけで、任意の呼び出し元が好きなことを何でもできます。あなたが知っているように、プログラミング。 –

+0

あなたはオブジェクト指向プログラミングに対する賛成票を持っているようです。特定の種類のソリューションに興味がないからといって、残りの人たちがコードを改善することに集中すべきではありません。 –

+0

私はOOPが大好きです。しかし、私はこのようなものを生計のために使うので、無駄な複雑さにはほとんど忍耐がありません。あなたは、MainWindowに* MainWindowが表示する可能性のあるすべてのAdd *ダイアログのために個別のインターフェイス実装をOPに追加しようとしますか?それともこれだけ?あなたのスキームが実際に「関数からオブジェクトを取得する」というセマンティクスが実際に達成することを説明できますか?それが*良いアイデアなら、なぜそれは一般的ではないのですか? 'IAddObserver '/'IAddObservable 'は、それぞれ6つをコピー/ペーストするよりもはるかに意味があります。またはスニペットを書く。 –

0

別の用途のMVVMの練習:

Solution structure

public class Person { 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public DateTime Birthday { get; set; } 
} 

public class PersonListViewModel : DependencyObject { 
    public ObservableCollection<Person> Items { get; set; } 

    public Person CurrentPerson 
    { 
     get { return (Person)GetValue(CurrentPersonProperty); } 
     set { SetValue(CurrentPersonProperty, value); } 
    } 
    public static readonly DependencyProperty CurrentPersonProperty = DependencyProperty.Register("CurrentPerson", typeof(Person), typeof(PersonListViewModel)); 

    public ICommand AddCommand { get; set; } 
    public ICommand EditCommand { get; set; } 

    public PersonListViewModel() { 
     Items = new ObservableCollection<Person>(); 

     AddCommand = new RelayCommand(p=> add()); 
     EditCommand = new RelayCommand(p=> { return CurrentPerson != null; }, p => edit()); 
    } 

    private void add() { 
     Person p= new Person(); 
     p.Id = Items.Count(); 
     p.Name = "New Name"; 
     p.Birthday = DateTime.Now; 
     Items.Add(p); 
    } 

    private void edit() { 
     var viewModel = new PersonItemViewModel(CurrentPerson); 
     var view = new View.PersonEditWindow(); 
     view.DataContext = viewModel; 
     view.Show(); 
    } 
} 

public class PersonItemViewModel : DependencyObject { 
    Person person; 

    public string Name 
    { 
     get { return (string)GetValue(NameProperty); } 
     set { SetValue(NameProperty, value); } 
    } 
    public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(PersonItemViewModel)); 

    public DateTime Birthday 
    { 
     get { return (DateTime)GetValue(BirthdayProperty); } 
     set { SetValue(BirthdayProperty, value); } 
    } 
    public static readonly DependencyProperty BirthdayProperty = DependencyProperty.Register("Birthday", typeof(DateTime), typeof(PersonItemViewModel)); 

    public PersonItemViewModel() { 

    } 
    public PersonItemViewModel(Person source) { 
     this.person = source; 

     Name = person.Name; 
     Birthday = person.Birthday; 
    } 

    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { 
     base.OnPropertyChanged(e); 

     if (e.Property == NameProperty) { 
      person.Name = (string) e.NewValue; 
     } 

     if (e.Property == BirthdayProperty) { 
      person.Birthday = (DateTime)e.NewValue; 
     } 
    } 
} 

リスト形式:

<Window x:Class="WpfApplication1.View.PersonListWindow" 
    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:local="clr-namespace:WpfApplication1.View" 
    mc:Ignorable="d" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    Title="PersonListWindow" Height="300" Width="300" 
    xmlns:viewModel="clr-namespace:WpfApplication1.ViewModel" 
    d:DataContext="{d:DesignInstance Type=viewModel:PersonListViewModel, IsDesignTimeCreatable=True}" 
    > 

<Grid Margin="8"> 
    <Grid.RowDefinitions> 
     <RowDefinition></RowDefinition> 
     <RowDefinition Height="Auto"></RowDefinition> 
    </Grid.RowDefinitions> 

    <DataGrid ItemsSource="{Binding Items}" SelectedItem="{Binding CurrentPerson, Mode=TwoWay}"> 
    </DataGrid> 

    <StackPanel Grid.Row="1" Height="32" Orientation="Horizontal" HorizontalAlignment="Center"> 
     <Button Margin="4" Command="{Binding AddCommand}">Add</Button> 
     <Button Margin="4" Command="{Binding EditCommand}">Edit</Button> 
    </StackPanel> 
</Grid> 

編集フォーム:

<Window x:Class="WpfApplication1.View.PersonEditWindow" 
    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:WpfApplication1.View" 
    mc:Ignorable="d" 
    Title="PersonEditWindow" Height="300" Width="300" 
      xmlns:viewModel="clr-namespace:WpfApplication1.ViewModel" 
    d:DataContext="{d:DesignInstance Type=viewModel:PersonItemViewModel, IsDesignTimeCreatable=True}" 

    > 
<Grid Margin="20"> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition></ColumnDefinition> 
     <ColumnDefinition></ColumnDefinition> 
    </Grid.ColumnDefinitions> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"></RowDefinition> 
     <RowDefinition Height="Auto"></RowDefinition> 
     <RowDefinition></RowDefinition> 
    </Grid.RowDefinitions> 


    <TextBlock Grid.Row="0">Name</TextBlock> 
    <TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Name}"></TextBox> 

    <TextBlock Grid.Row="1">Birthday</TextBlock> 
    <TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Birthday}"></TextBox> 

</Grid> 

Results (Image)

関連する問題