2017-05-24 211 views
0

我使用caliburn.micro MVVM框架開發WPF應用程序.. 在-爲了開發一個搜索屏幕,我需要的字段動態地加載到視圖,基於模型的屬性。MVVM動態添加字段到視圖

考慮下面視圖,視圖模型:

  • SearchViewModel
  • 搜索查看

假設T是在下面的例子一個類型的產品。

public class SearchViewModel<T> 
{ 
    public T Item{get;set;} 
} 

public class Product 
{ 
    public int Id{get;set;} 
    public string Name{get;set;} 
    public string Description{get;set;} 
} 

我有一個名爲SearchView.xaml的用戶控件,沒有內容。 每次加載視圖時,應將新字段添加到視圖中,並且字段應綁定到屬性。

根據上述代碼示例中,有3個公共屬性的產品類,因此文本框3應該被添加到動態的圖。當用戶在文本字段中輸入數據時,應該更新相應的屬性。

這可能嗎? 任何專家都可以通過提供一些示例幫助我實現這一目標嗎?

+0

所以基本上要生成的每個類型T的公共財產一個TextBox? – mm8

+0

@ mm8是的你是對的! – Rahul

+0

我想你知道如何將視圖的DataContext設置爲SearchViewModel ? – mm8

回答

1

我會提出不同要對此。相反,考慮動態地添加屬性視圖/模型,我會考慮增加有關這些屬性對視圖模型的列表。然後該列表將被綁定到ItemsControl,其模板看起來像是TextBox

所以您的視圖模型會對它的屬性爲您要檢查的「東西」。在此屬性的設置器中,使用反射枚舉您感興趣的屬性,並將某種FieldInfo類(您創建的)的實例添加到具有綁定的屬性列表中。

這有藏在心裏所有MVVM兼容太的利益,而且也沒有必要動態創建控件用自己的代碼。下面


的例子使用我自己的MVVM庫(作爲NuGet包),而不是caliburn.micro,但它應該是足夠相似遵循的基本思路。該示例的完整源代碼可以從this BitBucket repo下載。

正如您在附帶的屏幕截圖中看到的那樣,搜索字段是在視圖中動態創建的,而視圖中沒有任何代碼。一切都在視圖模型上完成。這也使您可以輕鬆訪問用戶輸入的數據。

視圖模型:

namespace DynamicViewExample 
{ 
    class MainWindowVm : ViewModel 
    { 
     public MainWindowVm() 
     { 
      Fields = new ObservableCollection<SearchFieldInfo>(); 
      SearchableTypes = new ObservableCollection<Type>() 
           { 
            typeof(Models.User), 
            typeof(Models.Widget) 
           }; 

      SearchType = SearchableTypes.First(); 
     } 

     public ObservableCollection<Type> SearchableTypes { get; } 
     public ObservableCollection<SearchFieldInfo> Fields { get; } 


     private Type _searchType; 

     public Type SearchType 
     { 
      get { return _searchType; } 
      set 
      { 
       _searchType = value; 
       Fields.Clear(); 
       foreach (PropertyInfo prop in _searchType.GetProperties()) 
       { 
        var searchField = new SearchFieldInfo(prop.Name); 
        Fields.Add(searchField); 
       } 
      } 
     } 

     private ICommand _searchCommand; 

     public ICommand SearchCommand 
     { 
      get { return _searchCommand ?? (_searchCommand = new SimpleCommand((obj) => 
      { 
       WindowManager.ShowMessage(String.Join(", ", Fields.Select(f => $"{f.Name}: {f.Value}"))); 
      })); } 
     } 
    } 
} 

SearchFieldInfo類:

namespace DynamicViewExample 
{ 
    public class SearchFieldInfo 
    { 
     public SearchFieldInfo(string name) 
     { 
      Name = name; 
     } 

     public string Name { get; } 

     public string Value { get; set; } = ""; 
    } 
} 

的視圖:

<Window 
    x:Class="DynamicViewExample.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:local="clr-namespace:DynamicViewExample" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    Title="MainWindow" 
    Width="525" 
    Height="350" 
    d:DataContext="{d:DesignInstance local:MainWindowVm}" 
    mc:Ignorable="d"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="*" /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 
     <ComboBox 
      Grid.Row="0" 
      ItemsSource="{Binding Path=SearchableTypes}" 
      SelectedItem="{Binding Path=SearchType}" /> 
     <ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Fields}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <StackPanel Orientation="Horizontal"> 
         <TextBlock Text="{Binding Path=Name}" /> 
         <TextBox Width="300" Text="{Binding Path=Value}" /> 
        </StackPanel> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
     <Button Grid.Row="2" Command="{Binding Path=SearchCommand}">Search</Button> 
    </Grid> 
</Window> 

模型類:

class User 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string PhoneNumber { get; set; } 
    public string Id { get; set; } 
} 

class Widget 
{ 
    public string ModelNumber { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

searching a Widget model searching User model

+0

Intresting,你能用一些代碼來解釋一下嗎? – Rahul

+0

我需要一點時間準備一些東西。 –

+0

這是我爲你創建的一個示例項目。我正在用最相關的部分更新我的答案。 https://bitbucket.org/BradleyUffner/wpf-dynamic-search-fields-example/src –

1

下面是一個基本示例,說明如何使用反射來生成的每個公共屬性TextBox

SearchView.xaml:

<Window x:Class="WpfApplication4.SearchView" 
     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:WpfApplication4" 
     mc:Ignorable="d" 
     Title="SearchView" Height="300" Width="300"> 
    <StackPanel x:Name="rootPanel"> 

    </StackPanel> 
</Window> 

SearchView.xaml.cs:

public partial class SearchView : UserControl 
{ 
    public SearchView() 
    { 
     InitializeComponent(); 
     DataContextChanged += SearchView_DataContextChanged; 
     DataContext = new SearchViewModel<Product>(); 
    } 

    private void SearchView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 
    { 
     if (e.NewValue != null) 
     { 
      Type genericType = e.NewValue.GetType(); 
      //check the DataContext was set to a SearchViewModel<T> 
      if (genericType.GetGenericTypeDefinition() == typeof(SearchViewModel<>)) 
      { 
       //...and create a TextBox for each property of the type T 
       Type type = genericType.GetGenericArguments()[0]; 
       var properties = type.GetProperties(); 
       foreach(var property in properties) 
       { 
        TextBox textBox = new TextBox(); 
        Binding binding = new Binding(property.Name); 
        if (!property.CanWrite) 
         binding.Mode = BindingMode.OneWay; 
        textBox.SetBinding(TextBox.TextProperty, binding); 

        rootPanel.Children.Add(textBox); 
       } 
      } 
     } 
    } 
} 

另一種選擇顯然是創建一個 「靜態」 的觀點爲每種類型的T並像往常一樣定義XAML標記中的TextBox元素。