[英]Can I use FakeItEasy to fake Properties.Settings.Default properties?
[英]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無法實現知道屬性值是否已更新。 如果使用如下所示的數據綁定,則可以使用BindingOperations
和BindingExpression
強制更新集合綁定。
但是,這樣做會有點麻煩,因為您將告訴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.