2016-03-26 17 views
0

Neredeyse ikna olduğum için çok tuhaf bir sorunum var.Bir UserControl örneğindeki UserControl kaynağının başka bir örnekle senkronize edilmesi

Üç UserControl'im var, FolderView, LocalFolderView, RemoteFolderView. LocalFolderView ve RemoteFolderView, her ikisi de FolderView'ı devralır ve sırasıyla LocalExplorer ve RemoteExplorer olmak üzere iki farklı denetimde kullanılır.

LocalExplorer/RemoteExplorer, FolderView'a bağlandığım dizelerin bir listesini içerir.

1'den fazla örneğim LocalExplorer/RemoteExplorer'ım olduğunda, her iki Gezgin için de FolderView'daki ListBox aynı öğeleri gösterir, ancak denetimlerin bağımlılık özellikleri farklıdır.

Kod gerçekten çok uzun olduğu için, olabildiğince yoğunlaşmaya çalışacağım. Şu anda, sorunun konulara bağlandığım gibi olduğuna inanıyorum.

LocalExplorer.xaml (RemoteExplorer.xaml özdeş bir yol izler,):

<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

Burada bu birden fazla, örneğin hata sergiler sahip kontrol imkanı sağlamaktadır. xaml.cs) benzer bir yol izler:

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 
} 

Sonraki tüm kaşifler özgü UI mantık ev ExplorerBase gibidir:

ExplorerBase.cs:

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); 
     } 
    } 
} 

I/Generic.xaml yaklaşım Temalar kullanarak şablon:

<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> 

Ve son olarak, ben hata neler olduğuna inanıyoruz FolderView. FolderView, kullanılan gerçek denetimler, LocalFolderView ve RemoteFolderView için bir temel oluşturur. Not, her iki LocalExplorer ve RemoteExplorer kullanmam veya kullanmamamdan bağımsız olarak hata oluşur. Aynı anda sadece iki örneği test ettim.

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 aynı izler):

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(); 
    } 
} 
not

Yapılacak :

  1. Her iki örnekte de FolderViews içindeki DataGrids aynı öğelerle doldurulur.
  2. Her bir FolderView örneğinin path özelliği farklıdır.
  3. Bu, yalnızca ikinci örneği öğelerle doldurduktan sonra ilkini yerleştirmeyi denedikten sonra gerçekleşir. İlk örneği ilk önce doldurursam, ikinci hiç bir şey olmaz.
  4. İki örneğin aynı öğelerle doldurulduğunu söylediğimde, ilkini doldurursam ilk öğenin ikinci sırada görüneceğini kastediyorum. Ve ikinciyi doldurursam, ikincisinin eşyaları ilk sırada görünür.
  5. Ayrıca, "populate" dediğimde, Path özelliğini FolderView olarak ayarlıyorum.

şeyler denedim:

  1. ben bağlamak şekilde değiştirilmesi. Örneğin, Binding ElementName=explorer, Path=Property gibi ciltleme yerine, onu Binding Property, RelativeSource={RelativeSource AncestorType={x:Type local:UserControlType}} olarak değiştirirdim.
  2. Çeşitli elemanlardan x:Name özelliklerini kaldırma.
  3. Damping FolderView özellikleri/değerleri. Yukarıdaki kaynakta bir örnek.

Dürüst olmak gerekirse, nasıl hata ayıklama yapacağımı bilmiyorum. Bu bir hata mı yoksa bağlayıcı mantığım çok mantıklı değil mi?

Düzenleme

İşte

İki Explorer örneklerini göstermek edebilirsiniz:

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

onlar ben de yanlışlıkla özellikle nasıl derinden dikkate başka bağlamak nasıl görmüyorum, hem kendi örnekleridir Verilen yuvalanmış ListBoxes görsel ağaçta.

cevap

1

Sorunlar, Öğeler özelliğinin bağımlılık özelliği kaydındadır.

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

Eğer kayıt statik ve Tip üzerine değil, örneğin kayıtlı olacak görebileceğiniz gibi. Sağlanan varsayılan değer new ObservableCollection<BindablePath>() olduğundan, aynı örnek FolderView'ın tüm örneklerinde paylaşılacaktır. Bu nedenle, herhangi bir yeni öğe eklendiğinde, her durumda, Öğeler özelliğinin aynı örneğe başvurduğu için her durumda gösterilmektedir.

Bir başparmak kuralı olarak, bağımlılık özelliği kaydı sırasında için herhangi bir başvuru türü için her zaman yeni örnek vermekten kaçınmalısınız.


Çözüm:

Yap varsayılan değer olarak geçersiz ve bunun yerine (örneğin başına) FolderView yapıcısı yeni örneğine olarak başlatılamadı.

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

Omg, TEŞEKKÜR EDERİZ. –

İlgili konular