簡體   English   中英

如何在代碼中將TextBoxes綁定到Properties.Settings.Default中的每個屬性?

[英]How can I bind TextBoxes to each property in Properties.Settings.Default in code?

我想動態生成一個文本框視圖,該視圖顯示Properties.Settings.Default中每個屬性的名稱和值。

我嘗試了以下代碼,但無法獲取正確分配的路徑。

foreach (SettingsProperty currentProperty in Properties.Settings.Default.Properties)
{
 TextBox X = new TextBox();
 TextBox Y = new TextBox();
 Grid G = new Grid();
 var x = Properties.Settings.Default[currentProperty.Name];

 X.SetBinding(ItemsControl.ItemsSourceProperty,
     new Binding
     {
         Source = "{x:Static properties:Settings.Default}" + ", Path = " + currentProperty.Name + "}",
         UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
     });

    //{Binding Source={x:Static properties:Settings.Default}, Path = MYSETTING1}

    G.Children.Add(X);
    MyStackPanel.Children.Add(G);

}

我還嘗試了以下方法:

X.SetBinding(ItemsControl.ItemsSourceProperty,
 new Binding
 {
     Source = "{x:Static properties:Settings.Default}",
     Path = currentProperty.Name,
     UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
 });

只是從您的第一句話開始,您可以執行以下操作:

foreach (SettingsProperty currentProperty in Properties.Settings.Default.Properties)
{
    TextBox X               = new TextBox();
    TextBox Y               = new TextBox();
    Grid G                  = new Grid();
    ColumnDefinition one    = new ColumnDefinition();
    ColumnDefinition two    = new ColumnDefinition();
    one.Width               = new GridLength(50, GridUnitType.Star);
    two.Width               = new GridLength(50, GridUnitType.Star);
    G.ColumnDefinitions.Add(one);
    G.ColumnDefinitions.Add(two);
    Grid.SetColumn(X, 0);
    Grid.SetColumn(Y, 1);

    X.SetBinding(TextBox.TextProperty,
        new Binding
        {
            Source = currentProperty.Name,
            Path = new PropertyPath("."),
            UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        });

    Y.SetBinding(TextBox.TextProperty,
        new Binding
        {
            Source = currentProperty.DefaultValue,
            Path = new PropertyPath("."),
            UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        });

    G.Children.Add(X);
    G.Children.Add(Y);
    MyStackPanel.Children.Add(G);
}

您可以通過多種方法來執行此操作。 但正如此評論所暗示的:

還有一個更大的問題:為什么要在代碼中綁定它們? 通常嘗試這樣的事情表明您走錯了軌道。 WPF在設計時就考慮了MVVM模式,如果您使用它,則永遠不需要這樣做。

…您應該繼續使用常規綁定。 WPF使這相對簡單,並且可以完全在XAML中實現:

<Window x:Class="TestSO57299808PropertyBrowser.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:properties="clr-namespace:TestSO57299808PropertyBrowser.Properties"
        xmlns:configuration="clr-namespace:System.Configuration;assembly=System"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
  <ListBox ItemsSource="{Binding PropertyValues, Source={x:Static properties:Settings.Default}}">
    <ListBox.ItemTemplate>
      <DataTemplate DataType="{x:Type configuration:SettingsPropertyValue}">
        <Grid ShowGridLines="True">
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <TextBlock Text="{Binding Name}" Padding="5,0" Grid.Column="0"/>
          <TextBlock Text="{Binding PropertyValue}" Padding="5,0" Grid.Column="1"/>
        </Grid>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</Window>

上面的代碼將一個ListBox放入窗口,將其ItemsSource屬性Settings.Default.PropertyValues為引用Settings.Default.PropertyValues集合,並聲明一個DataTemplate用於顯示該集合中包含的每個SettingsPropertyValue對象的屬性名稱和值。

其實我說謊 因為.NET Settings對象有一個小錯誤,所以僅需要一行代碼。 在首次檢索任何屬性值之前, PropertyValues集合將為空。 因此,您需要確保在初始化期間檢索屬性值(任何屬性都可以)。 例如,在窗口構造函數中:

    public MainWindow()
    {
        var _ = Properties.Settings.Default.Property1;
        InitializeComponent();
    }

請參閱為什么設置PropertyValues有0個項目? 有關該錯誤的一些討論。

請記住,無論是在代碼隱藏中復制值還是如上所述使用綁定,如果/當屬性值更改時,您都將需要處理自己的更新,因為集合本身沒有實現INotifyCollectionChanged ,因此WPF無法實現知道屬性值是否已更新。 如果使用如下所示的數據綁定,則可以使用BindingOperationsBindingExpression強制更新集合綁定。

但是,這樣做會有點麻煩,因為您將告訴WPF每次僅更改一個屬性就更新整個集合。 如果您希望屬性值發生變化並希望在UI中反映出來,那么最好實現一個代理,該代理可以提供正確的屬性名稱/值對集合以及屬性更改通知。 例如:

class ViewModel
{
    public IReadOnlyList<SettingsPropertyValueProxy> Values { get; } = Array.AsReadOnly(
            Properties.Settings.Default.Properties
            .Cast<SettingsProperty>()
            .Select(p => new SettingsPropertyValueProxy(p.Name))
            .OrderBy(p => p.Name)
            .ToArray());
}

class SettingsPropertyValueProxy : INotifyPropertyChanged
{
    public string Name { get; }
    public object PropertyValue => Properties.Settings.Default[Name];

    public SettingsPropertyValueProxy(string name)
    {
        Name = name;
        Properties.Settings.Default.PropertyChanged += (sender, e) => _OnPropertyChanged(e.PropertyName);
    }

    private void _OnPropertyChanged(string propertyName)
    {
        if (propertyName == Name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PropertyValue)));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

然后在XAML中,直接綁定到視圖模型而不是設置對象:

<Window x:Class="TestSO57299808PropertyBrowser.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:l="clr-namespace:TestSO57299808PropertyBrowser"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
  <Window.DataContext>
    <l:ViewModel/>
  </Window.DataContext>

  <ListBox ItemsSource="{Binding Values}">
    <ListBox.ItemTemplate>
      <DataTemplate DataType="{x:Type l:SettingsPropertyValueProxy}">
        <Grid ShowGridLines="True">
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <TextBlock Text="{Binding Name}" Padding="5,0" Grid.Column="0"/>
          <TextBlock Text="{Binding PropertyValue}" Padding="5,0" Grid.Column="1"/>
        </Grid>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</Window>

上面與先前示例的唯一區別是,將數據上下文設置為視圖模型的實例,然后將視圖模型的Values屬性用於ItemsSource而不是Settings.Default.PropertyValues屬性。

請注意,在這種方法中,無需任何技巧即可觸發PropertyValues集合的填充,因為此版本完全不依賴該集合。

這比編寫后台代碼創建UI對象並手動實現其綁定好得多。 在XAML中執行此操作,僅需執行代碼隱藏操作即可以一種WPF友好的方式包裝設置,從而確保將UI與基礎數據正確分離,並使按需更改UI本身變得容易得多。

最后,如果這不僅僅是一個學術活動,請注意,已經存在顯示對象屬性的解決方案。 參見WPF中的wpf propertyGrid類似WinForms Propertygrid嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM