2016-10-24 5 views
0

Я попытался создать somthing, чтобы быстро найти и посмотреть файлы. Поэтому я создал TreeView, у которого StackPanels в качестве элементов. StackPanel содержит изображение a a Label.Как отсортировать древовидную структуру с элементами stackpanel

private TreeViewItem createFile(string Name, string soureFile) 
    { 
     TreeViewItem tvi = new TreeViewItem(); 
     StackPanel sp = new StackPanel(); 
     Image i = new Image(); 
     Label l_Text = new Label(); 
     Label l_FileName = new Label(); 

     l_FileName.Content = soureFile; 
     l_FileName.Width = 0; 
     l_Text.Content = Name; 

     System.Drawing.Bitmap dImg = (System.Drawing.Bitmap)Properties.Resources.ResourceManager.GetObject("Picture"); 
     MemoryStream ms = new MemoryStream(); 
     dImg.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); 
     BitmapImage bImg = new BitmapImage(); 
     bImg.BeginInit(); 
     bImg.StreamSource = new MemoryStream(ms.ToArray()); 
     bImg.EndInit(); 
     i.Source = bImg; 
     i.Height = 20; 
     i.Width = 20; 

     sp.Name = "SP_File"; 
     sp.Orientation = Orientation.Horizontal; 
     sp.Children.Add(i); 
     sp.Children.Add(l_Text); 
     sp.Children.Add(l_FileName); 
     tvi.Header = sp; 

     return tvi; 
    } 

Можно создавать логические папки (только для создания структуры) и добавлять файлы и другие папки в папки (только ссылки на фактический файл на жестком диске). Это работало нормально, пока я не попытался сортировать TreeView. Я прочитал материал на на сортировочные TreeViews с

SortDescriptions.Add(new SortDescription("Header", ListSortDirection.Ascending)); 

Очевидно, что это не работает для меня, так как я не могу обмена «заголовок» с «Header.StackPanel.Label.Text» Как я прочитал немного дальше, кажется, Я использовал неправильный подход ко всему этому, не используя MVVM (Numerically sort a List of TreeViewItems in C#).

Поскольку у меня нет опыта работы с MVVM, кто-нибудь может объяснить мне, как лучше всего это делать с MVVM? Я использую Список «watchedFile» для хранения файлов и папок.

Я в основном имею следующий класс для файла

class watchedFile 
{ 
    public string name { get; private set; } 
    public string path { get; private set; } 
    public List<string> tags { get; private set; } 

    public watchedFile(string Name, string Path, List<string> Tags) 
    { 
     name = Name; 
     path = Path; 
     tags = Tags; 
    }   
} 

Если путь является недействительным или опустошить сво папку. TreeViewItem имеет небольшое изображение, которое показывает небольшую «папку» или «изображение», метку, которая показывает «watchedFile.name» и невидимую метку, которая содержит файл watchedfile.path (который отображается только как всплывающая подсказка). Думаю, я должен сделать это с помощью DataBinding, поэтому мне не нужно добавлять невидимую метку.

Вопросы:

  1. Как я могу решить задачу с помощью MVVM?
  2. Как я могу привязать изображение к TreeViewItem, когда у меня есть только wacthedFile.path, чтобы отличить?
  3. Как отсортировать наблюдаемые элементы?
  4. Как отслеживать уровень TreeView (так что я могу перестроить структуру из сохраненного файла)?
  5. Есть ли способ сортировать TreeView с помощью элементов StackPanel без использования MVVM/привязки данных?

Любая помощь очень ценится.

ответ

1

Вот как это сделать MVVM.

Во-первых, напишите классы вида. Здесь у нас есть основная модель просмотра, в которой есть коллекция WatchedFile экземпляров, а затем у нас есть класс WatchedFile. Я также решил сделать класс Tag вместо того, чтобы просто использовать строки. Это позволяет нам писать шаблоны данных в XAML, которые явно и исключительно будут использоваться для отображения экземпляров Tag, а не строк в целом. Пользовательский интерфейс полон строк.

Свойства уведомления являются утомительными для записи, если у вас нет фрагмента. I have snippets (Украдите их! Они не прибиты!).

Как только вы получили это, сортировка не имеет большого значения. Если вы хотите отсортировать элементы корневого уровня, это WatchedFile.

SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending)); 

Но мы сделаем это в XAML ниже.

Сериализация также проста: просто сделайте классы классов просмотра сериализуемыми. Важно то, что для сортировки и сериализации вам не нужно заботиться о том, что находится в шаблонах элементов treeview. StackPanels, GroupBoxes, что угодно - это вообще не имеет значения, потому что ваш код сортировки и сериализации просто касается ваших классов данных, а не элементов интерфейса. Вы можете существенно изменить визуальные детали в шаблонах данных, не беспокоясь об этом, затрагивая любую другую часть кода. Вот что приятно в MVVM.

ViewModels:

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Linq; 
using System.Runtime.CompilerServices; 
using System.Text; 
using System.Threading.Tasks; 

namespace WatchedFile.ViewModels 
{ 
    public class ViewModelBase : INotifyPropertyChanged 
    { 
     public event PropertyChangedEventHandler PropertyChanged; 

     protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 
     { 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    public class WatchedFile : ViewModelBase 
    { 
     #region Name Property 
     private String _name = default(String); 
     public String Name 
     { 
      get { return _name; } 
      set 
      { 
       if (value != _name) 
       { 
        _name = value; 
        OnPropertyChanged(); 
       } 
      } 
     } 
     #endregion Name Property 

     #region Path Property 
     private String _path = default(String); 
     public String Path 
     { 
      get { return _path; } 
      set 
      { 
       if (value != _path) 
       { 
        _path = value; 
        OnPropertyChanged(); 
       } 
      } 
     } 
     #endregion Path Property 

     #region Tags Property 
     private ObservableCollection<Tag> _tags = new ObservableCollection<Tag>(); 
     public ObservableCollection<Tag> Tags 
     { 
      get { return _tags; } 
      protected set 
      { 
       if (value != _tags) 
       { 
        _tags = value; 
        OnPropertyChanged(); 
       } 
      } 
     } 
     #endregion Tags Property 
    } 

    public class Tag 
    { 
     public Tag(String value) 
     { 
      Value = value; 
     } 
     public String Value { get; private set; } 
    } 

    public class MainViewModel : ViewModelBase 
    { 
     public MainViewModel() 
     { 
      Populate(); 
     } 

     public void Populate() 
     { 
      // Arbitrary test info, just for display. 
      WatchedFiles = new ObservableCollection<WatchedFile> 
      { 
       new WatchedFile() { Name = "foobar.txt", Path = "c:\\testfiles\\foobar.txt", Tags = { new Tag("Testfile"), new Tag("Text") } }, 
       new WatchedFile() { Name = "bazfoo.txt", Path = "c:\\testfiles\\bazfoo.txt", Tags = { new Tag("Testfile"), new Tag("Text") } }, 
       new WatchedFile() { Name = "whatever.xml", Path = "c:\\testfiles\\whatever.xml", Tags = { new Tag("Testfile"), new Tag("XML") } }, 
      }; 
     } 

     #region WatchedFiles Property 
     private ObservableCollection<WatchedFile> _watchedFiles = new ObservableCollection<WatchedFile>(); 
     public ObservableCollection<WatchedFile> WatchedFiles 
     { 
      get { return _watchedFiles; } 
      protected set 
      { 
       if (value != _watchedFiles) 
       { 
        _watchedFiles = value; 
        OnPropertyChanged(); 
       } 
      } 
     } 
     #endregion WatchedFiles Property 
    } 
} 

код позади. Примечание. Я добавил только одну строку к тому, что дал мне волшебник.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 

namespace WatchedFile 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      DataContext = new ViewModels.MainViewModel(); 
     } 
    } 
} 

И, наконец, XAML:

<Window 
    x:Class="WatchedFile.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:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase" 
    xmlns:local="clr-namespace:WatchedFile" 
    xmlns:vm="clr-namespace:WatchedFile.ViewModels" 
    mc:Ignorable="d" 
    Title="MainWindow" Height="350" Width="525"> 
    <Window.Resources> 
     <CollectionViewSource 
       x:Key="SortedWatchedFiles" 
       Source="{Binding WatchedFiles}"> 
      <CollectionViewSource.SortDescriptions> 
       <scm:SortDescription PropertyName="Name" Direction="Ascending" /> 
      </CollectionViewSource.SortDescriptions> 
     </CollectionViewSource> 
    </Window.Resources> 
    <Grid> 
     <TreeView 
      ItemsSource="{Binding Source={StaticResource SortedWatchedFiles}}" 
      > 
      <TreeView.Resources> 
       <HierarchicalDataTemplate 
        DataType="{x:Type vm:WatchedFile}" 
        ItemsSource="{Binding Tags}" 
        > 
        <TextBlock 
         Text="{Binding Name}" 
         ToolTip="{Binding Path}" 
         /> 
       </HierarchicalDataTemplate> 
       <HierarchicalDataTemplate 
        DataType="{x:Type vm:Tag}" 
        > 
        <TextBlock 
         Text="{Binding Value}" 
         /> 
       </HierarchicalDataTemplate> 
      </TreeView.Resources> 
     </TreeView> 
    </Grid> 
</Window> 

Часть XAML менее очевидна. TreeView.Resources подходит для любого ребенка из TreeView. HierarchicalDataTemplate s не имеют свойства x:Key, что делает их неявным. Это означает, что когда объекты TreeView создаются, каждый из корневых элементов будет иметь экземпляр класса WatchedFile для своего DataContext. Поскольку WatchedFile имеет неявный шаблон данных, который будет использоваться для заполнения его содержимого. TreeView является рекурсивным, поэтому он использует HierarchicalDataTemplate вместо обычного DataTemplate. HierarchicalDataTemplate добавляет свойство ItemSource, в котором говорится о товаре, где следует искать его детей на своем объекте DataContext. WatchedFile.Tags - это ItemsSource для элементов дерева корневого уровня. Это Tag, который имеет свой собственный неявный HierarchicalDataTemplate. У него нет детей, поэтому он не использует HierarchicalDataTemplate.ItemsSource.

Поскольку все наши коллекции являются ObservableCollection<T>, вы можете добавлять и удалять элементы из любой коллекции в любое время, и пользовательский интерфейс будет автоматически обновляться. Вы можете сделать то же самое с Name и Path свойствами WatchedFile, поскольку он реализует INotifyPropertyChanged и его свойства повышают PropertyChanged при изменении их значений. Пользовательский интерфейс XAML подписывается на все уведомления, не сообщая ему, и делает правильные вещи - предполагая, что вы сказали ему, что ему нужно знать, чтобы сделать это.

Ваш отделенного кода может захватить SortedWatchedFiles с FindResource и изменить его SortDescriptions, но это имеет смысл для меня, так как это агностик о том, как вы наполнении TreeView: за

<Button Content="Re-Sort" Click="Button_Click" HorizontalAlignment="Left" /> 

    <!-- ... snip ... --> 

    <TreeView 
     x:Name="WatchedFilesTreeView" 
     ...etc. as before... 

Код:

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    var cv = CollectionViewSource.GetDefaultView(WatchedFilesTreeView.ItemsSource); 

    cv.SortDescriptions.Clear(); 
    cv.SortDescriptions.Add(
     new System.ComponentModel.SortDescription("Name", 
      System.ComponentModel.ListSortDirection.Descending)); 
} 
+0

спасибо за ваш очень подробный ответ - мне это очень нравится. пока он работает хорошо, и я хочу, чтобы я получил немного больше в MVVM. у меня все еще были проблемы. Я хотел бы, чтобы файл/папка имел несколько записей.поэтому я добавил наблюдаемую коллекцию к WatchedFile – Thoms

+0

, пожалуйста, см. следующий «ответ» для дальнейших вопросов. – Thoms

+1

жаль, что я не смог правильно прочитать;) заменил 'ItemsSource = {{Binding Tags}" 'на' ItemsSource = "{Binding Subs}" ' – Thoms