У меня есть C# WPF PropertyGrid (Denys Vuika), который представляет собой простой пример для закрепления logi c для более крупной реализации. Мой общий подход - создать сетку и использовать SelectedObject, который является клоном реальных свойств в качестве отображаемых / редактируемых свойств. Во время редактирования настоящие Свойства остаются нетронутыми - изменяется только Клон. Пользователь может отменить или подтвердить редактирование. Отмена приведет к тому, что клон перезагрузит реальные свойства, по сути, отменив все изменения. OK установит те же свойства, что и у редактируемого клонирования. Будет расширенный набор действий, но для простейшего примера все, что нужно - «Отмена» и «ОК».
Я могу успешно создавать, отображать и редактировать сетку. Однако я обнаружил, что использование Real vs Clone, похоже, вызывает проблемы со значениями отображаемых / сохраняемых свойств.
Пример поведения: свойство bool истинно в Real и заменено на false (изменение, по идее, должно произойти только в клоне). Отмена должна оставить свойство как истинное, но фактически изменяет его на ложное. Есть и другие странные поведения аналогичного характера, когда состояние свойства кажется неправильным после действия редактирования. В этом примере я использую bools снова для простоты, но то же самое происходит и с другими типами данных.
Вот код для окна PropertyGrid:
internal class TESTPropertiesGrid : Window
{
private Button btnOK = null;
private Button btnCancel = null;
internal PropertyGrid pgrProperties = null;
private clsTestProperties pclPropertiesSource = null;
private clsTestProperties pclPropertiesClone = null;
// --- Constructor ---
public TESTPropertiesGrid(clsTestProperties pSourceProperties)
{
pclPropertiesSource = pSourceProperties;
Caption = "Test Editing Properties";
Width = 390;
Height = 320;
Content = LoadXAML("TestPropertiesGrid.xaml");
Closing += OnWindow_Closing;
}
private void OnWindow_Closing(object sender, CancelEventArgs e)
{
btnOK.Click -= OnOK;
btnCancel.Click -= OnCancel;
btnOK = null;
btnCancel = null;
pclPropertiesSource = null;
pclPropertiesClone = null;
pgrProperties = null;
}
private DependencyObject LoadXAML(string xamlFilename)
{
try
{
using (FileStream fs = new FileStream(xamlFilename, FileMode.Open))
{
Page page = System.Windows.Markup.XamlReader.Load(fs) as Page;
if (page == null)
return null;
DependencyObject pageContent = page.Content as DependencyObject;
btnOK = LogicalTreeHelper.FindLogicalNode(pageContent, "btnOK") as Button;
btnCancel = LogicalTreeHelper.FindLogicalNode(pageContent, "btnCancel") as Button;
pgrProperties = LogicalTreeHelper.FindLogicalNode(pageContent, "pgrDashboardProperties") as PropertyGrid;
btnOK.Click += OnOK;
btnCancel.Click += OnCancel;
ReloadProperties();
return pageContent;
}
}
catch (Exception erk)
{
// Output erk.ToString()
return null;
}
}
// --- Apply all changes to the active Properties, hide the editor ---
private void OnOK(object sender, RoutedEventArgs e)
{
this.Hide();
pclPropertiesSource = pclPropertiesClone; // Save the Properties
ReloadProperties();
}
// --- Ignore all changes, hide the editor ---
private void OnCancel(object sender, RoutedEventArgs e)
{
this.Hide();
ReloadProperties();
}
// --- Reload the Properties from the active Properties ---
private void ReloadProperties()
{
// -- Clone the class so any changes are not pushed to the active Properties until OK'ed --
pclPropertiesClone = null;
pclPropertiesClone = pclPropertiesSource.Clone() as clsTestProperties;
pgrProperties.Dispatcher.InvokeAsync(() =>
{
try
{
pgrProperties.SelectedObject = null;
pgrProperties.SelectedObject = pclPropertiesClone;
pgrProperties.InvalidateVisual();
}
catch (Exception eek)
{
// Output eek.ToString()
}
});
}
}
Вот код для класса Properties. Обратите внимание, что он содержит свойство на верхнем уровне и вложенное свойство. Я обнаружил, что поведение верхнего уровня и вложенных bools кажется другим.
public class clsTestProperties : INotifyPropertyChanged, ICloneable
{
// -- Constructor to Set Defaults Properties --
public clsTestProperties()
{
SelectedTestExpander = new TestExpander();
SelectedTestExpander.NestedProperty = true;
TestTopLevel = true;
}
// Event to flag that Property items have changed and the Property Grid should be regenerated
public event PropertyChangedEventHandler PropertyChanged;
// Event Handler to force an update to the Property Grid after changes to any Property
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// --- Create an identical copy of this structure ---
public object Clone()
{
return this.MemberwiseClone();
}
[Display(GroupName = "Test", Order = 0, Name = "NON-Nested Property")]
public bool TestTopLevel
{ get; set; }
[Display(GroupName = "Test", Order = 1, Name = "Expandable Category")]
public TestExpander SelectedTestExpander
{ get; set; }
[TypeConverter(typeof(ExpandableObjectConverter))]
public class TestExpander
{
public TestExpander()
{
}
public override string ToString()
{
return string.Format("");
}
[Display(Order = 0, Name = "Nested Property")]
public bool NestedProperty { get; set; }
}
}
И вот как я создаю экземпляр окна TESTPropertiesGrid. Это происходит после нажатия кнопки в более крупном приложении, не включенном здесь.
internal TESTPropertiesGrid wndPropertiesGrid = null;
internal clsTestProperties pclTestProperties = new clsTestProperties();
private WindowInteropHelper wihWndProperties = null;
// --- Display the Property Grid for editing ---
private void EditProperties()
{
if (wndPropertiesGrid == null || // Never created
wihWndProperties == null || // Never created either
wihWndProperties.Handle == IntPtr.Zero) // Window was Closed
{
wndPropertiesGrid = new TESTPropertiesGrid(this.pclTestProperties);
wihWndProperties = new WindowInteropHelper(wndPropertiesGrid);
}
wndPropertiesGrid.Dispatcher.InvokeAsync(new Action(() =>
{
try
{
wndPropertiesGrid.Show();
}
catch (Exception erk)
{
// Output erk.ToString()
}
}));
}
Вот XAML для окна TESTPropertiesGrid:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pg="http://schemas.denisvuyka.wordpress.com/wpfpropertygrid">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="5" />
<RowDefinition Height="*" />
<RowDefinition Height="45" />
<RowDefinition Height="5" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalScrollBarVisibility="Auto">
<pg:PropertyGrid x:Name="pgrProperties"></pg:PropertyGrid>
</ScrollViewer>
<StackPanel Grid.Column="1" Grid.Row="2" HorizontalAlignment="Right" Orientation="Horizontal" VerticalAlignment="Bottom">
<Button x:Name="btnOK" Margin="5" Width="52" Content="OK"/>
<Button x:Name="btnCancel" Margin="5" Width="52" Content="Cancel"/>
</StackPanel>
</Grid>
</Page>
Я не включил фактические методы вывода поскольку они настраиваются в контексте более крупного приложения.
Надеюсь, все вышеперечисленное показывает, что я делаю (и пытаюсь сделать), и может быть протестировано кем угодно на форуме. У меня такое ощущение, что я наблюдаю поведение, которое вызвано «скрытыми» действиями, с которыми я не знаком и которых, возможно, не ожидаю. Кажется, есть разница в поведении между верхним уровнем и вложенными свойствами, которую я не понимаю. Это все, что может быть. ).
Мы будем благодарны за любой совет / помощь!