2016-03-26 7 views
0

私はほとんどバグであると確信している本当に奇妙な問題があります。別のインスタンスと同期する1つのUserControlインスタンスのUserControlへのバインド

私は3つのUserControl、FolderView、LocalFolderView、RemoteFolderViewを持っています。 LocalFolderViewとRemoteFolderViewはどちらもFolderViewを継承し、それぞれ2つの他のコントロール、LocalExplorerとRemoteExplorerで使用されます。

LocalExplorer/RemoteExplorerには、FolderViewにバインドする文字列のリストがあります。

問題は、LocalExplorer/RemoteExplorerのインスタンスが2つ以上ある場合は、ExplorerViewのFolderViewのListBoxに同じ項目が表示されますが、コントロールの依存関係のプロパティは一見異なります。

コードは実際には長いので、できる限り凝縮しようと思います。現在、問題は私が物事を縛っている方法であると信じています。

LocalExplorer.xaml(RemoteExplorer.xamlが同一のパターンに従う):

<UserControl x:Class="LocalExplorer" 
      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:MyNamespace" 
      mc:Ignorable="d" 
      Width="Auto" 
      Height="Auto" 
      ClipToBounds="True" 
      x:Name="explorer"> 
    <local:ExplorerBase Path="{Binding ElementName=explorer, Path=Path}" Orientation="{Binding ElementName=explorer, Path=Orientation}"> 
     <local:ExplorerBase.FolderView> 
      <local:LocalFolderView x:Name="FolderView" Path="{Binding Path, RelativeSource={RelativeSource AncestorType={x:Type local:LocalExplorer}}}"/> 
     </local:ExplorerBase.FolderView> 
    </local:ExplorerBase> 
</UserControl> 

LocalExplorer.xaml.cs(RemoteExplorer

は、ここで私はそれの複数のインスタンスは、バグを示していコントロールです。 xaml.cs)は同じパターンに従う:

public partial class Explorer : UserControl 
{ 
    #region Explorer 

    public Explorer() 
    { 
     InitializeComponent(); 
    } 

    #endregion 

    #region Dependency Properties 

    public static DependencyProperty PathProperty = DependencyProperty.Register("Path", typeof(string), typeof(Explorer), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 
    public string Path 
    { 
     get 
     { 
      return (string)GetValue(PathProperty); 
     } 
     set 
     { 
      SetValue(PathProperty, value); 
     } 
    } 

    #endregion 
} 

次に、すべての探検に特定のUIロジックを収容ExplorerBase、である。

ExplorerBase.cs:私はそれがテーマ/ Generic.xamlのアプローチを使用してテンプレート

public partial class ExplorerBase : Control 
{ 
    public ExplorerBase() 
    { 
     this.DefaultStyleKey = typeof(ExplorerBase); 
    } 

    public override void OnApplyTemplate() 
    { 
     base.ApplyTemplate(); 
    } 

    public static readonly DependencyProperty FolderViewProperty = DependencyProperty.Register("FolderView", typeof(object), typeof(ExplorerBase), null); 
    public object FolderView 
    { 
     get 
     { 
      return GetValue(FolderViewProperty); 
     } 
     set 
     { 
      SetValue(FolderViewProperty, value); 
     } 
    } 

    public static DependencyProperty PathProperty = DependencyProperty.Register("Path", typeof(string), typeof(ExplorerBase), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 
    public string Path 
    { 
     get 
     { 
      return (string)GetValue(PathProperty); 
     } 
     set 
     { 
      SetValue(PathProperty, value); 
     } 
    } 
} 

<Style TargetType="{x:Type Imagin.Controls:ExplorerBase}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="Imagin.Controls:ExplorerBase"> 
       <ContentPresenter Content="{TemplateBinding FolderView}"/> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

そして最後に、私は信じてFolderViewは、バグを持っているものです。 FolderViewは、使用される実際のコントロールLocalFolderViewとRemoteFolderViewのベースです。このバグは、LocalExplorerとRemoteExplorerの両方を使用するかどうかにかかわらず発生します。私は一度に合計2つのインスタンスだけをテストしました。

FolderView.xaml:

<UserControl x:Class="FolderView" 
      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" 
      mc:Ignorable="d" 
      Height="Auto" 
      Width="Auto" 
      x:Name="folderView"> 
    <UserControl.Resources> 
     <BooleanToVisibilityConverter x:Key="BoolToVisibility" /> 
     <Imagin.Data:InverseBooleanToVisibilityConverter x:Key="InverseBoolToVisibility" /> 
     <Grid> 
      <ListBox x:Name="ListBox" AllowDrop="True" ItemsSource="{Binding Path=Items, RelativeSource={RelativeSource AncestorType={x:Type local:FolderView}}}" SelectionMode="Extended" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" IsSynchronizedWithCurrentItem="True"> 
       <ListBox.ItemsPanel> 
        <ItemsPanelTemplate> 
         <WrapPanel IsItemsHost="True"/> 
        </ItemsPanelTemplate> 
       </ListBox.ItemsPanel> 
       <ListBox.Resources> 
        <DataTemplate DataType="{x:Type local:BindableFile}"> 
         <local:Thumbnail FilePath="{Binding Path}" IsCheckBoxEnabled="False" ToolTip="{Binding ToolTip}" Title="{Binding Name}" Width="Auto" Height="Auto"/> 
        </DataTemplate> 
        <DataTemplate DataType="{x:Type local:BindableFolder}"> 
         <local:Thumbnail FilePath="{Binding Path}" IsCheckBoxEnabled="False" ToolTip="{Binding ToolTip}" Title="{Binding Name}" Width="Auto" Height="Auto"/> 
        </DataTemplate> 
       </ListBox.Resources> 
       <ListBox.ItemContainerStyle> 
        <Style TargetType="{x:Type ListBoxItem}"> 
         <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
        </Style> 
       </ListBox.ItemContainerStyle> 
      </ListBox> 
      <DataGrid x:Name="DataGrid" ItemsSource="{Binding Items, RelativeSource={RelativeSource AncestorType={x:Type local:FolderView}}}" AutoGenerateColumns="False" BorderThickness="0" AlternationCount="2" GridLinesVisibility="None" HeadersVisibility="Column" CanUserAddRows="False" CanUserResizeColumns="True" IsSynchronizedWithCurrentItem="True" AllowDrop="True"> 
      </DataGrid> 
     </Grid> 
</UserControl> 

FolderView.xaml.cs:

public abstract partial class FolderView : UserControl 
{ 
    #region DependencyProperties 

    public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<BindablePath>), typeof(FolderView), new FrameworkPropertyMetadata(new ObservableCollection<BindablePath>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 
    public ObservableCollection<BindablePath> Items 
    { 
     get 
     { 
      return (ObservableCollection<BindablePath>)GetValue(ItemsProperty); 
     } 
     set 
     { 
      SetValue(ItemsProperty, value); 
     } 
    } 

    public static DependencyProperty PathProperty = DependencyProperty.Register("Path", typeof(string), typeof(FolderView), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnPathChanged)); 
    public string Path 
    { 
     get 
     { 
      return (string)GetValue(PathProperty); 
     } 
     set 
     { 
      SetValue(PathProperty, value); 
     } 
    } 
    private static void OnPathChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e) 
    { 
     FolderView FolderView = (FolderView)Object; 
     FolderView.Refresh(); 
     FolderView.SearchTextBox.Text = string.Empty; 
    } 

    #endregion 

    #region Methods 

    public virtual void GetItems(string Path, out List<string> Folders, out List<string> Files) 
    { 
     Folders = default(List<string>); 
     Files = default(List<string>); 
    } 

    /// <summary> 
    /// Refreshes current path with contents. 
    /// </summary> 
    public virtual void Refresh() 
    { 
     //Used to debug property values at runtime. So far the values for each object instance are unique. 
     foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(this)) 
     { 
      string name = descriptor.Name; 
      object value = descriptor.GetValue(this); 
      Console.WriteLine("{0}={1}", name, value); 
     } 
    } 

    /// <summary> 
    /// Populates controls with actual items via binding. We must do this on UI thread. This occurs immediately after <Refresh()>. 
    /// </summary> 
    /// <param name="Folders"></param> 
    /// <param name="Files"></param> 
    public virtual void Populate(List<FtpListItem> Folders, List<FtpListItem> Files) 
    { 
    } 

    public virtual void Populate(List<string> Folders, List<string> Files) 
    { 
    } 

    #endregion 

    #region FolderView 

    public FolderView() 
    { 
     InitializeComponent(); 
    } 

    #endregion 
} 

LocalFolderView.cs(RemoteFolderView.csは同じパターンに従う):注意すべき

public sealed class LocalFolderView : FolderView 
{ 
    public override void GetItems(string Path, out List<string> Folders, out List<string> Files) 
    { 
     //These are my own functions 
     Folders = Directory.GetDirectories(Path); 
     Files = Directory.GetFiles(Path, null); 
    } 

    public override void Populate(List<string> Folders, List<string> Files) 
    { 
     int NumFolders = Folders.Count, NumFiles = Files.Count; 
     this.IsEmpty = NumFolders == 0 && NumFiles == 0 ? true : false; 
     if (Folders == null || Files == null || (NumFolders == 0 && NumFiles == 0)) return; 
     for (int j = 0, Count = NumFolders; j < Count; j++) 
     { 
      this.Items.Add(new BindableFolder(Folders[j])); 
     } 
     for (int j = 0, Count = NumFiles; j < Count; j++) 
     { 
      this.Items.Add(new BindableFile(Files[j])); 
     } 
    } 

    public override void Refresh() 
    { 
     base.Refresh(); 
     this.Items.Clear(); 
     //If directory doesn't exist, we don't want to enter it. 
     if (!System.IO.Directory.Exists(this.Path)) return; 
     List<string> Folders = null; 
     List<string> Files = null; 
     string CurrentPath = this.Path; 

     BackgroundWorker Worker = new BackgroundWorker(); 
     Worker.DoWork += (s, e) => 
     { 
      this.GetItems(CurrentPath, out Folders, out Files); 
     }; 
     Worker.RunWorkerCompleted += (s, e) => 
     { 
      //Start populating items 
      var DispatcherOperation = Application.Current.Dispatcher.BeginInvoke(new Action(() => this.Populate(Folders, Files))); 
     }; 
     Worker.RunWorkerAsync(); 
    } 
} 

観光:

  1. 両方のインスタンスのFolderView内のDataGridにも同じ項目が設定されます。
  2. 各FolderViewインスタンスのパスプロパティが異なります。
  3. これは、2番目のインスタンスにアイテムを入力した後で、最初のアイテムにアイテムを挿入しようとした場合にのみ発生します。私が最初のインスタンスを最初に移入すると、2番目のインスタンスに何も起こりません。
  4. 私は両方のインスタンスが同じアイテムで作成されていると言います。つまり、最初のアイテムを作成すると、最初のアイテムが2番目のアイテムに表示されます。そして、もし私が2番目のものを埋めると、2番目のアイテムが最初に現れます。
  5. また、 "populate"と言うと、私はFolderViewにPathプロパティを設定していることを意味します。

物事は私が試してみた:

  1. 私はバインドする方法を変更します。たとえば、Binding ElementName=explorer, Path=Propertyのようにバインドする代わりに、Binding Property, RelativeSource={RelativeSource AncestorType={x:Type local:UserControlType}}に変更します。
  2. x:Nameの属性をさまざまな要素から削除します。
  3. FolderViewのプロパティ/値をダンプします。上記のソースの例です。

正直なところ、どのようにデバッグするのか分かりません。これはバグですか、そうではありませんか?

編集

ここ

私は2つのエクスプローラインスタンスを表示する方法は次のとおりです。

<local:LocalExplorer /> 
<local:RemoteExplorer/> 

彼らは、私はどちらかが誤って、特にどのように深く考えると、他に特異的に結合することができるかが表示されない、両方の自分自身のインスタンスで考えますリストボックスはビジュアルツリー内にネストされています。

答えて

1

問題はItemsプロパティの依存関係プロパティの登録にあります。

public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", 
typeof(ObservableCollection<BindablePath>), typeof(FolderView), 
new FrameworkPropertyMetadata(new ObservableCollection<BindablePath>(), 
        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 

あなたは登録は静的であり、タイプではなく、インスタンスに登録されます見ることができるように。提供されるデフォルト値はnew ObservableCollection<BindablePath>()なので、同じインスタンスがFolderViewのすべてのインスタンスで共有されます。そのため、新しい項目が追加されるたびに、Itemsプロパティは同じインスタンスを参照しているため、すべてのインスタンスに表示されます。

従属プロパティの登録時に参照型の新しいインスタンスを に指定しないでください。


ソリューション

メイクのデフォルト値としてnull、代わりに(インスタンスごと)FolderViewのコンストラクタから新しいインスタンスにそれを初期化します。

new FrameworkPropertyMetadata(null,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 
+1

Omg、ありがとうございます。 –

関連する問題