当前位置:网站首页>[winui3] Écrivez une copie du gestionnaire de fichiers Explorer
[winui3] Écrivez une copie du gestionnaire de fichiers Explorer
2022-04-23 04:50:00 【Lin Xiao.】
J'ai utiliséVueJ'ai écrit une imitationExplorerGestionnaire de fichiers,Voir l'articleAvecVueÉcrivez une simple imitationExplorerGestionnaire de fichiers_Lin Petit blog-CSDNBlogs_vueSystème de gestion des documents
Cette fois.MicrosoftWindows UI Bibliothèque (WinUI) 3 Pour reconstruire une version de bureauExplorerGestionnaire de fichiers.
Un.、Principes de navigation
Utilisez d'abord la barre de navigation et regardez attentivement,Nous avons plusieurs façons d'opérer:
- Cliquez sur“Vers le Haut”Bouton retour à la table des matières précédente,Cliquez sur le nom du dossier dans la barre d'adresse pour retourner à n'importe quel répertoire
- Double - cliquez sur le dossier pour entrer dans le nouveau répertoire
- Cliquez sur“En avant!”,“Reculez!”Navigation par bouton - poussoir
Où aller de l'avant,Marche arrière,Vous pouvez cliquer sur le petit triangle pour voir une liste,Cliquez pour entrer dans le dossier,La liste enregistre l'historique de navigation,Même entrer dans le même dossier à plusieurs reprises,La liste est toujours enregistrée,Comme le montre la figure ci - dessous::

Donc nous pouvons analyser et résumer deux variables:
- Une variable pour stocker la navigation réelle(navigationStack)
- Une autre variable utilisée pour stocker l'historique de navigation(navigationHistoryStack)
La pile de navigation est utilisée pour stocker des informations pour chaque dossier de navigation,Assemblez ces dossiers pour former le chemin actuel, Un ensemble simple de<li>Les éléments naviguent sur la pile en liant,Pour former une barre d'adresses(webDans le monde aussi appelé la navigation des miettes de pain)C'est.
navigationStackEn fait, c'est une pile,C'est une sortie avancée(FILO)Principes
L'historique de navigation n'enregistre que la trajectoire de l'utilisateur,Pas d'impact sur les cibles de navigation,Comme je viens de le dire,Même entrer dans le même dossier à plusieurs reprises,La liste est toujours enregistrée
navigationHistoryStackEn fait, c'est une file d'attente,Avec le premier entré, premier sorti(FIFO)Principes
Ensuite, on commence le Code
On va en construire un nouveauWinUI3Projets,NomméExplorerNavigation

Créer un nouveauObservableCollection<T>Type dérivé deObservableCollectionEx<T>,Et ensuite réaliser:
- push() La méthode peut ajouter un ou plusieurs éléments à la fin d'un tableau
- shift() Méthode pour supprimer le premier élément du tableau
- unshift() La méthode peut ajouter un ou plusieurs éléments à l'avant du tableau
- pop() Méthode pour supprimer le dernier élément du tableau
EtForEach()Traversée,EtIndexOf() Méthodes communes telles que l'interrogation des marques d'angle :
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
public ObservableCollectionEx() : base()
{
}
public ObservableCollectionEx(IEnumerable<T> collection) : base(collection)
{
}
public ObservableCollectionEx(List<T> list) : base(list)
{
}
public void ForEach(Action<T> action)
{
for (int index = 0; index < this.Count; ++index)
action(this[index]);
}
public int IndexOf(Predicate<T> match)
{
var startIndex = 0;
for (int index = startIndex; index < this.Count; ++index)
{
if (match(this[index]))
{
return index;
}
}
return -1;
}
public void Shift()
{
if (this.Count == 0)
{
return;
}
this.Remove(this.First());
}
public void Unshift(T item)
{
this.Insert(0, item);
}
public void Pop()
{
if (this.Count == 0)
{
return;
}
this.Remove(this.Last());
}
public void Push(T item)
{
this.Add(item);
}
NouveauExplorerViewModelType, Il sera utilisé comme Explorer Base de la page de navigation ViewModel
ÉtablissementNavigationStack、NavigationHistoryStack Objet et initialisation dans le constructeur :
public ExplorerViewModel()
{
NavigationStack = new ObservableCollectionEx<IExplorerItem>();
NavigationHistoryStack = new ObservableCollectionEx<IExplorerItem>();
}
private ObservableCollectionEx<IExplorerItem> _navigationStack;
public ObservableCollectionEx<IExplorerItem> NavigationStack
{
get { return _navigationStack; }
set
{
_navigationStack = value;
OnPropertyChanged(nameof(NavigationStack));
}
}
private ObservableCollectionEx<IExplorerItem> _navigationHistoryStack;
public ObservableCollectionEx<IExplorerItem> NavigationHistoryStack
{
get { return _navigationHistoryStack; }
set
{
_navigationHistoryStack = value;
OnPropertyChanged(nameof(NavigationHistoryStack));
}
}
2.、Principe du saut de dossier
Commençons par la structure de données suivante
public interface IExplorerItem
{
ObservableCollection<IExplorerItem> Children { get; set; }
bool IsCurrent { get; set; }
bool IsExpanded { get; set; }
string Name { get; set; }
string Path { get; set; }
List<string> PathStack { get; }
ExplorerItemType Type { get; set; }
}
IExplorerItem Est une classe de description d'arbre définie ,Supposons qu'on ait un tas d'arbres de fichiers qui poussent comme ça:

L'objet arborescent correspondant est :
IExplorerItem root = new ExplorerItem()
{
Children = new ObservableCollection<IExplorerItem>() {
new ExplorerItem() {
Children = new ObservableCollection<IExplorerItem>() {
new ExplorerItem() {
Name = "DossiersB",
Path = "Mon disque réseau/DossiersA/DossiersB",
Type = ExplorerItemType.Folder
},
new ExplorerItem() {
Children = new ObservableCollection<IExplorerItem>() {
new ExplorerItem() {
Name = "DossiersD",
Path = "Mon disque réseau/DossiersA/DossiersC/DossiersD",
Type = ExplorerItemType.Folder
},
},
Name = "DossiersC",
Path = "Mon disque réseau/DossiersA/DossiersC",
Type = ExplorerItemType.Folder
},
new ExplorerItem() {
Name = "Documentation3",
Path = "Mon disque réseau/DossiersA/Documentation3",
Type = ExplorerItemType.File
}
},
Name = "DossiersA",
Path = "Mon disque réseau/DossiersA",
Type = ExplorerItemType.Folder
},
new ExplorerItem() {
Name = "Documentation1",
Path = "Mon disque réseau/Documentation1",
Type = ExplorerItemType.File
},
new ExplorerItem() {
Name = "Documentation2",
Path = "Mon disque réseau/Documentation2",
Type = ExplorerItemType.File
}
},
Name = "Mon disque réseau",
Path = "Mon disque réseau",
Type = ExplorerItemType.Folder
};
Trois、 Écrire une interface arborescente :
App.cs Définir l'injection dépendante dans
public App()
{
this.InitializeComponent();
InitializeComponent();
// Register services
if (!_initialized)
{
_initialized = true;
Ioc.Default.ConfigureServices(
new ServiceCollection()
//ViewModels
.AddSingleton<MainPageViewModel>()
.BuildServiceProvider());
}
}
NouveauMainPageViewModel.cs
InMainWindow.xaml.cs Lier l'objet contextuel de la page à MainPageViewModel
public MainWindow()
{
this.InitializeComponent();
this.MainFrame.DataContext = Ioc.Default.GetRequiredService<MainPageViewModel>();
}
InMainWindow.xaml Écrire un contrôle arborescent
<TreeView
ItemsSource="{Binding RootExplorerItems}" SelectedItem="{Binding CurrentExplorerItem ,Mode=TwoWay}"
ItemInvoked="TreeView_ItemInvoked"
SelectionMode="Single"
Margin="0,12"
>
<TreeView.ItemTemplate>
<DataTemplate
x:DataType="model:ExplorerItem">
<TreeViewItem AutomationProperties.Name="{Binding Name}"
ItemsSource="{Binding Children}" IsExpanded="True" >
<StackPanel Orientation="Horizontal">
<Image Width="20" Source="/Assets/folder.png" Visibility="{Binding Type, Converter={StaticResource isValueToVisibilityConverter}, ConverterParameter=Folder}"/>
<Image Width="20" Source="/Assets/file.png" Visibility="{Binding Type, Converter={StaticResource isValueToVisibilityConverter}, ConverterParameter=File}"/>
<TextBlock Margin="0,0,10,0"/>
<TextBlock Style="{StaticResource NavigationTextBlockStyle}"
Text="{Binding Name}" />
</StackPanel>
</TreeViewItem>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Interface de rendu

Quatre、Écrivez la logique de navigation
Méthode de traitement de la pile de navigation :NavigationTo(IExplorerItem folder)
On vient d'analyser le principe de navigation,Le rôle de la pile de navigation est de former l'adresse,Nous définissons une logique de traitement de la pile de navigation:
- Déterminer si la page courante est dans la pile de navigation
- Si,Apparaît à l'emplacement de la cible dans la pile de navigation
- Si non,Appuyez sur la pile de navigation
Parmi euxToFolder Méthode utilisée pour naviguer et rafraîchir la page ,Plus tard
public virtual void NavigationTo(IExplorerItem folder)
{
DealWithNavigationStack(folder);
if (ToFolder(folder))
{
NavigationHistoryStack.ForEach((element) =>
{
element.IsCurrent = false;
});
folder.IsCurrent = true;
PushNavigationHistoryStack(folder);
}
}
“Vers le Haut”Méthodes de navigation:NavigationBack()
L'action vers le Haut appartient à un traitement spécifique de la pile de navigation:
- L'entrée la plus haute apparaît directement,
- Obtenez l'entrée supérieure et naviguez
private void NavigationBack()
{
if (NavigationStack.Count == 1)
{
return;
}
NavigationStack.Pop();
var lastItem = NavigationStack.LastOrDefault();
if (lastItem == null)
{
return;
}
if (ToFolder(lastItem))
{
NavigationHistoryStack.ForEach((element) =>
{
element.IsCurrent = false;
});
lastItem.IsCurrent = true;
PushNavigationHistoryStack(lastItem);
}
}
Méthode de saut: ToFolder,
De nombreuses méthodes ultérieures se réfèrent à cette méthode ,Cette fonction effectue simplement un saut,Objet de description du fichier entrant,Effectuer la navigation,Rafraîchir la page,RetourboolLa valeur représente le succès ou non:
public virtual bool ToFolder(IExplorerItem item)
{
if (item == null || item.Path == CurrentExplorerItem.Path)
{
return false;
}
var currentExplorerItem = NavigationStack.FirstOrDefault(c => c.Path == item.Path);
if (currentExplorerItem == null)
{
return false;
}
CurrentExplorerItem = currentExplorerItem;
return true;
}
Cinq、Écrire une logique de traitement de navigation historique
“Reculez!”Méthodes: NavigationHistoryBack()
- Déterminez d'abord où se trouve la page courante dans la navigation historique
- Après avoir reçu l'étiquette d'angle+1(Parce que c'est la file d'attente,Donc plus tôt l'angle est grand),Obtenez la dernière entrée de page dans la file d'attente de navigation historique, Et exécuter la méthode de navigation
private void NavigationHistoryBack()
{
var currentIndex = NavigationHistoryStack.IndexOf(
(c) => c.IsCurrent
);
if (currentIndex < NavigationHistoryStack.Count - 1)
{
var forwardIndex = currentIndex + 1;
var folder = NavigationHistoryStack[forwardIndex];
DealWithNavigationStack(folder);
if (ToFolder(folder))
{
NavigationHistoryStack.ForEach((element) =>
{
element.IsCurrent = false;
});
NavigationHistoryStack[forwardIndex].IsCurrent = true;
}
}
}
“En avant!”Méthodes:NavigationHistoryForward()
- Déterminez d'abord où se trouve la page courante dans la navigation historique
- Après avoir reçu l'étiquette d'angle-1(Parce que c'est la file d'attente,Donc plus tard, plus petit sera le coin.),Obtenez l'entrée de page précédente dans la file d'attente de navigation historique, Et exécuter la méthode de navigation
private void NavigationHistoryForward()
{
var currentIndex = NavigationHistoryStack.IndexOf(
(c) => c.IsCurrent
);
if (currentIndex > 0)
{
var forwardIndex = currentIndex - 1;
var folder = NavigationHistoryStack[forwardIndex];
DealWithNavigationStack(folder);
if (ToFolder(folder))
{
NavigationHistoryStack.ForEach((element) =>
{
element.IsCurrent = false;
});
NavigationHistoryStack[forwardIndex].IsCurrent = true;
}
}
}
Et nous avons besoin d'un moyen ,Utilisé pour afficher dans la file d'attente historique(En cours)Étiquettes:
public virtual bool GetIsCurrentHistoryNavigationItem(IExplorerItem item)
{
var result = item.IsCurrent;
return result;
}
Six、 Écrire l'interface de la barre de navigation :
InMainPageViewModel.cs Trois sont définis dans Command Liaison des boutons pour le Front End
public RelayCommand NavigationHistoryBackCommand { get; private set; }
public RelayCommand NavigationHistoryForwardCommand { get; private set; }
public RelayCommand NavigationBackCommand { get; private set; }
public ExplorerViewModel()
{
NavigationHistoryBackCommand = new RelayCommand(NavigationHistoryBack);
NavigationHistoryForwardCommand = new RelayCommand(NavigationHistoryForward);
NavigationBackCommand = new RelayCommand(NavigationBack);
}
In MainWindow.xamlMoyenne Compilation3Les boutons“En avant!”,“Reculez!”,“Vers le Haut”, Et lié au CommandAllez.
<StackPanel Orientation="Horizontal" >
<Button Style="{StaticResource NavigationBarButtonStyle}" Command="{Binding NavigationHistoryBackCommand}" Margin="10">
<FontIcon Style="{StaticResource NavigationBarButtonTextStyle}" Glyph=""/>
</Button>
<Button Style="{StaticResource NavigationBarButtonStyle}" Command="{Binding NavigationHistoryForwardCommand}" Margin="10" >
<FontIcon Style="{StaticResource NavigationBarButtonTextStyle}" Glyph=""/>
</Button>
<Button Style="{StaticResource NavigationBarButtonStyle}" Command="{Binding NavigationBackCommand}" Margin="10" >
<FontIcon Style="{StaticResource NavigationBarButtonTextStyle}" Glyph=""/>
</Button>
</StackPanel>
InMainPageViewModel.csDéfinition moyennePathStack
private ObservableCollection<string> _pathStack;
public ObservableCollection<string> PathStack
{
get { return _pathStack; }
set
{
_pathStack = value;
OnPropertyChanged(nameof(PathStack));
}
}
In MainWindow.xamlÉcrivez unBreadcrumbBar Comme zone d'entrée de la barre de navigation
<BreadcrumbBar VerticalAlignment="Center" HorizontalAlignment="Stretch" ItemsSource="{Binding PathStack}">
</BreadcrumbBar>
Interface de rendu:

InMainPageViewModel.csDéfinition moyenneCurrentFileInfos Comme liste de fichiers sous le chemin courant ,DéfinitionSelectedFileInfo Comme fichier actuellement sélectionné .
private ObservableCollectionEx<IFileInfo> _currentFileInfos;
public ObservableCollectionEx<IFileInfo> CurrentFileInfos
{
get { return _currentFileInfos; }
set
{
_currentFileInfos = value;
OnPropertyChanged(nameof(CurrentFileInfos));
}
}
private IFileInfo _selectedFileInfo;
public IFileInfo SelectedFileInfo
{
get { return _selectedFileInfo; }
set
{
_selectedFileInfo = value;
OnPropertyChanged(nameof(SelectedFileInfo));
}
}
In MainWindow.xamlÉcrit enGridView Présentation du contenu du catalogue
<!--File Content-->
<GridView
x:Name="BasicGridView"
IsItemClickEnabled="True"
ItemsSource="{Binding CurrentFileInfos}"
SelectedItem="{Binding SelectedFileInfo}"
ItemClick="BasicGridView_ItemClick"
SelectionMode="Single">
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="5, 7, 5, 7"/>
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemTemplate>
<DataTemplate>
<Grid Width="100" Height="120" DoubleTapped="Grid_DoubleTapped">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Image Grid.Row="0" Visibility="{Binding Type, Converter={StaticResource isValueToVisibilityConverter}, ConverterParameter=1}" Width="90" Source="/Assets/folder.png"/>
<Image Grid.Row="0" Visibility="{Binding Type, Converter={StaticResource isValueToVisibilityConverter}, ConverterParameter=2}" Width="90" Source="/Assets/file.png"/>
<TextBlock HorizontalAlignment="Center" Grid.Row="1" Text="{Binding FileName, Converter={StaticResource absoluteNameConverter}}" Style="{ThemeResource CaptionTextBlockStyle}"></TextBlock>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Lorsque vous double - cliquez sur un élément dans la zone de fichier , Besoin d'ouvrir le fichier , Ou sauter dans le répertoire
InMainWindow.xaml.cs Pour écrire une liaison d'événement en double - clic
private async void Grid_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
var targetFile = (e.OriginalSource as FrameworkElement).DataContext as IFileInfo;
if (targetFile == null)
{
return;
}
if (targetFile.Type == FileInfoType.Folder)
{
var targetFolder = (this.MainFrame.DataContext as MainPageViewModel).CurrentExplorerItem.Children.FirstOrDefault(c => c.Name == targetFile.FileName);
if (targetFolder != null)
{
(this.MainFrame.DataContext as MainPageViewModel).NavigationTo(targetFolder);
}
}
else
{
ContentDialog subscribeDialog = new ContentDialog
{
Title = $" Vous avez ouvert le fichier [{targetFile.FileName}]",
CloseButtonText = "C'est sûr.",
DefaultButton = ContentDialogButton.Primary
};
subscribeDialog.XamlRoot = App.Window.Content.XamlRoot;
ContentDialogResult result = await subscribeDialog.ShowAsync();
}
}
Interface de rendu:
Effet final:

Entrepôt de code:
jevonsflash/ExplorerNavigation (github.com)
Conclusion:
C'est une simple navigation de fichier Demo, Vous pouvez créer des explorateurs de fichiers non locaux ,Pour remplacerWindows énorme et gonflé explorer.exe, Vous pouvez également créer un client de disque réseau ,ftp Outils et autres outils de gestion de fichiers en ligne .WinUI3 Est le dernier cadre de développement Microsoft Desktop , Le Code de ce projet peut également être facilement réécrit WpfOuUWPApplication.
Seven Niu Cloud upload Tool :
版权声明
本文为[Lin Xiao.]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230449458663.html
边栏推荐
- 数据孤岛是什么?为什么2022年仍然存在数据孤岛?
- C list field sorting contains numbers and characters
- Leetcode003 -- judge whether an integer is a palindrome number
- Leetcode006 -- find the longest common prefix in the string array
- Small volume Schottky diode compatible with nsr20f30nxt5g
- Case of using stream load to write data to Doris
- 【数据库】MySQL单表查询
- 拼了!两所A级大学,六所B级大学,纷纷撤销软件工程硕士点!
- Spark optimization
- Innovation training (IV) preliminary preparation - server
猜你喜欢

Mysql50 basic exercises

Innovative practice of short video content understanding and generation technology in meituan

AQS源码阅读

Learning Android from scratch -- Introduction

Innovation training (V) configuration information

数据孤岛是什么?为什么2022年仍然存在数据孤岛?

Learning Android V from scratch - UI

Small volume Schottky diode compatible with nsr20f30nxt5g

Unity rawimage background seamlessly connected mobile

Sword finger offer: the median in the data stream (priority queue large top heap small top heap leetcode 295)
随机推荐
Progress of innovation training (IV)
New terminal play method: script guidance independent of technology stack
Small volume Schottky diode compatible with nsr20f30nxt5g
解决ValueError: Argument must be a dense tensor: 0 - got shape [198602], but wanted [198602, 16].
Alibaba tip: it is better to create threads manually
Teach you how to build the ruoyi system by Tencent cloud
Implementation of switching windows and capturing data in selenium mode
Leetcode005 -- delete duplicate elements in the array in place
Painless upgrade of pixel series
Innovation training (VI) routing
PIP3 installation requests Library - the most complete pit sorting
The object needs to add additional attributes. There is no need to add attributes in the entity. The required information is returned
Solutions to the failure of sqoop connection to MySQL
Solve valueerror: argument must be a deny tensor: 0 - got shape [198602], but wanted [198602, 16]
Programmers complain: I really can't live with a salary of 12000. Netizen: how can I say 3000
leetcode004--罗马数字转整数
IEEE Transactions on industrial information (TII)
js 判断数字字符串中是否含有字符
C language: Advanced pointer
Opencv + clion face recognition + face model training
