Copyables
というCopyable
(カスタムクラス)のObservableCollection
のユーザー設定からデータを取得するWPF MVVMアプリケーションがあります。メインビューモデル(ClipboardAssistantViewModel
)内で、CollectionViewSource
のソースをCopyables
に設定しました。これは、メインビュー(MainWindow
)のItemsControl
にバインドされます。こののDataTemplate
は、基本的にボタンですが、データとコマンドをバインドできるプロパティが付いている「CopyableControl」というユーザーコントロールです。このメソッドが初めて呼び出されたときに初めてユーザー設定が保存されるのはなぜですか?
ユーザがCopyableControl
をクリックすると、ビューモデル(DefineCopyableViewModel
)はClipboardAssistantViewModel
におけるそれらのObservableCollection
に添加し、そのコレクションをMainWindow
でItemsControl
に結合されます。これのDataTemplate
は、DefineCopyableControl
と呼ばれるUserControl
であり、クリックされたCopyable
に関連する現在の値がDefineCopyableControl
のテキストボックスにバインドされて編集されるように設定されています。
私の問題:のみ最初の実行に取り組んでいますDefineCopyableViewModel
、EditCopyable()
における方法は、(その仕事はどんな編集が行われていると、ユーザーが「OK」ををクリックした後、ユーザー設定を保存することです)があります。 CopyableControl
をクリックして編集したら、「OK」をクリックしてもう一度クリックしてもう一度編集し、「OK」をクリックしてアプリケーションを閉じてもう一度開くと、最初の編集だけが完了しています保存されました(UIが編集された値で両方の時間で更新された場合でも)。データバインディングを「リフレッシュ」する必要があると思われます。私の知見はこのコードの中のコメントを参照してください。
次のように私のコードは次のとおりです。
モデル:
namespace ClipboardAssistant.Models
{
public class Copyable : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation
private string name;
public string Name
{
get { return name; }
set
{
if (value != name)
{
name = value;
NotifyPropertyChanged("Name");
}
}
}
private string textToCopy;
public string TextToCopy
{
get { return textToCopy; }
set
{
if (value != textToCopy)
{
textToCopy = value;
NotifyPropertyChanged("TextToCopy");
}
}
}
public Copyable() { }
public Copyable(string Name, string TextToCopy)
{
this.Name = Name;
this.TextToCopy = TextToCopy;
}
}
}
のviewmodels:
namespace ClipboardAssistant.ViewModels
{
public class ClipboardAssistantViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged Implementation
public CollectionViewSource CopyablesView { get; set; }
public ObservableCollection<DefineCopyableViewModel> Definers { get; set; }
public CopyableClickCommand CopyableClickCommand { get; set; }
public ClipboardAssistantViewModel()
{
Definers = new ObservableCollection<DefineCopyableViewModel>();
CopyablesView = new CollectionViewSource();
CopyablesView.Source = Properties.Settings.Default.Copyables;
CopyableClickCommand = new CopyableClickCommand(this);
EditModeClickCommand = new EditModeClickCommand(this);
}
public void RefreshCopyables()
{
// Both these methods of refreshing appear to have the same effect.
Properties.Settings.Default.Copyables = (ObservableCollection<Copyable>)CopyablesView.Source;
CopyablesView.Source = Properties.Settings.Default.Copyables;
}
public void EditCopyable(Copyable Copyable)
{
Definers.Add(new DefineCopyableViewModel(Copyable, this));
}
}
}
namespace ClipboardAssistant.ViewModels
{
public class DefineCopyableViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged Implementation
public ClipboardAssistantViewModel MyParent { get; set; }
public Copyable Copyable { get; set; }
public DefinerOKClickCommand DefinerOKClickCommand { get; set; }
public DefineCopyableViewModel(Copyable Copyable, ClipboardAssistantViewModel MyParent)
{
this.Copyable = Copyable;
this.MyParent = MyParent;
DefinerOKClickCommand = new DefinerOKClickCommand(this);
}
public void EditCopyable()
{
// Refresh, save, no refresh, save -> doesn't save second edit.
// Save, refresh, save, no refresh -> does save second edit.
MessageBoxResult r = MessageBox.Show("Refresh?", "Refresh", MessageBoxButton.YesNo);
if (r == MessageBoxResult.Yes)
{
MyParent.RefreshCopyables();
}
// These two MessageBox methods (save and refresh) can be swapped around (see above comments).
MessageBoxResult s = MessageBox.Show("Save?", "Save", MessageBoxButton.YesNo);
if (s == MessageBoxResult.Yes)
{
Properties.Settings.Default.Save();
}
MyParent.Definers.Remove(this);
}
}
}
メインウィンドウ:
<Window x:Class="ClipboardAssistant.Views.MainWindow" x:Name="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:vm="clr-namespace:ClipboardAssistant.ViewModels"
xmlns:ctrls="clr-namespace:ClipboardAssistant.Controls"
mc:Ignorable="d"
Title="Clipboard Assistant" Height="400" Width="700">
<Window.DataContext>
<vm:ClipboardAssistantViewModel />
</Window.DataContext>
<Grid>
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="20" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding CopyablesView.View}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ctrls:CopyableControl Copyable="{Binding}"
ClickCopyable="{Binding DataContext.CopyableClickCommand, ElementName=mainWindow}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<DockPanel Grid.Row="2">
<Button x:Name="btnEditCopyableMode" HorizontalAlignment="Left" DockPanel.Dock="Left"
Content="Edit" Margin="0,0,10,0" Command="{Binding EditModeClickCommand}" />
</DockPanel>
</Grid>
<ItemsControl ItemsSource="{Binding Definers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ctrls:DefineCopyableControl Copyable="{Binding DataContext.Copyable}"
ClickCancel="{Binding DataContext.DefinerCancelClickCommand, ElementName=mainWindow}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
CopyableControl:
を<UserControl x:Class="ClipboardAssistant.Controls.CopyableControl" x:Name="copyableControl"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ClipboardAssistant.Controls"
mc:Ignorable="d" d:DesignHeight="75" d:DesignWidth="200">
<Grid Width="200" Height="75">
<Button Command="{Binding ClickCopyable, ElementName=copyableControl}"
CommandParameter="{Binding Copyable, ElementName=copyableControl}"
Content="{Binding Copyable.Name, ElementName=copyableControl}"
Style="{StaticResource CopyableMainButtonStyle}" />
</Grid>
</UserControl>
namespace ClipboardAssistant.Controls
{
public partial class CopyableControl : UserControl
{
public static readonly DependencyProperty ClickCopyableProperty =
DependencyProperty.Register("ClickCopyable", typeof(ICommand), typeof(CopyableControl));
public ICommand ClickCopyable
{
get { return (ICommand)GetValue(ClickCopyableProperty); }
set { SetValue(ClickCopyableProperty, value); }
}
public static readonly DependencyProperty CopyableProperty =
DependencyProperty.Register("Copyable", typeof(Copyable), typeof(CopyableControl));
public Copyable Copyable
{
get { return (Copyable)GetValue(CopyableProperty); }
set { SetValue(CopyableProperty, value); }
}
public CopyableControl()
{
InitializeComponent();
}
}
}
DefineCopyableControl:
<UserControl x:Class="ClipboardAssistant.Controls.DefineCopyableControl" x:Name="defineCopyableControl"
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:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500">
<Grid x:Name="MainGrid" Background="Blue">
<Grid Width="200" Height="180">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="10" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="20" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Name" Foreground="White" />
<TextBox Grid.Row="1" Text="{Binding Copyable.Name}" x:Name="tbN" />
<Label Grid.Row="3" Content="Copyable Text" Foreground="White" />
<TextBox Grid.Row="4" Text="{Binding Copyable.TextToCopy}" x:Name="tbTTC" />
<DockPanel Grid.Row="6">
<Button Width="70" Content="OK" DockPanel.Dock="Right" HorizontalAlignment="Right"
Command="{Binding DefinerOKClickCommand}"
CommandParameter="{Binding ElementName=defineCopyableControl}" />
</DockPanel>
</Grid>
</Grid>
</UserControl>
public partial class DefineCopyableControl : UserControl
{
public static readonly DependencyProperty CopyableProperty =
DependencyProperty.Register("Copyable", typeof(Copyable), typeof(DefineCopyableControl));
public Copyable Copyable
{
get { return (Copyable)GetValue(CopyableProperty); }
set { SetValue(CopyableProperty, value); }
}
public DefineCopyableControl()
{
InitializeComponent();
}
}
コマンド:
namespace ClipboardAssistant.ViewModels.Commands
{
public class CopyableClickCommand : ICommand
{
public ClipboardAssistantViewModel ViewModel { get; set; }
public CopyableClickCommand(ClipboardAssistantViewModel viewModel)
{
ViewModel = viewModel;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
Copyable cp = (Copyable)parameter;
ViewModel.EditCopyable(cp);
}
}
}
namespace ClipboardAssistant.ViewModels.Commands
{
public class DefinerOKClickCommand : ICommand
{
public DefineCopyableViewModel ViewModel { get; set; }
public DefinerOKClickCommand(DefineCopyableViewModel viewModel)
{
ViewModel = viewModel;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
ViewModel.EditCopyable();
}
}
}
セッティング:
namespace ClipboardAssistant.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.ObjectModel.ObservableCollection<ClipboardAssistant.Models.Copyable> Copyables {
get {
return ((global::System.Collections.ObjectModel.ObservableCollection<ClipboardAssistant.Models.Copyable>)(this["Copyables"]));
}
set {
this["Copyables"] = value;
}
}
}
}
私は、デザイナーが提供する 'Settings'オブジェクトに' ObservableCollection 'のようなものを保存するように納得させるのは苦痛だと知っています。私にそうする必要がある質問。あなたのコード例は、設定のコピー/ペーストのストレート実装なしでは不完全です。さらに、私は上記のすべてが実際に問題を再現することが本当に必要であると真剣に疑う。コード例も最小限ではないようです。 –
コード例に設定を追加しました。 –