diff --git a/Directory.Build.props b/Directory.Build.props
index b4062adb5049..5731e23a25bd 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -9,6 +9,7 @@
10.0.26100.67-preview
$(TargetFrameworkVersion)-windows$(TargetWindowsVersion)
preview
+ nullable
true
diff --git a/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs b/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs
index decf6588f52b..aa99018edafc 100644
--- a/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs
+++ b/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs
@@ -15,6 +15,6 @@ public interface IOmnibarTextMemberPathProvider
/// Retrieves the path of the text member as a string. This path can be used to identify the location of the text member.
///
/// Returns a string representing the path of the text member.
- string GetTextMemberPath(string textMemberPath);
+ string? GetTextMemberPath(string textMemberPath);
}
}
diff --git a/src/Files.App.Controls/Omnibar/Omnibar.cs b/src/Files.App.Controls/Omnibar/Omnibar.cs
index 707dd90bfc58..7e7528f1e84f 100644
--- a/src/Files.App.Controls/Omnibar/Omnibar.cs
+++ b/src/Files.App.Controls/Omnibar/Omnibar.cs
@@ -275,7 +275,7 @@ private string GetObjectText(object obj)
return obj is string text
? text
: obj is IOmnibarTextMemberPathProvider textMemberPathProvider
- ? textMemberPathProvider.GetTextMemberPath(CurrentSelectedMode.TextMemberPath ?? string.Empty)
+ ? textMemberPathProvider.GetTextMemberPath(CurrentSelectedMode.TextMemberPath ?? string.Empty) ?? string.Empty
: obj.ToString() ?? string.Empty;
}
diff --git a/src/Files.App.Controls/Sidebar/ISidebarItemModel.cs b/src/Files.App.Controls/Sidebar/ISidebarItemModel.cs
index eedd91e8f4d1..9d2a3038f262 100644
--- a/src/Files.App.Controls/Sidebar/ISidebarItemModel.cs
+++ b/src/Files.App.Controls/Sidebar/ISidebarItemModel.cs
@@ -1,10 +1,24 @@
// Copyright (c) Files Community
// Licensed under the MIT License.
+using Microsoft.UI.Xaml;
+
namespace Files.App.Controls
{
+ public interface ISidebarItemPresentationModel
+ {
+ string? Text { get; }
+
+ object? ToolTip { get; }
+
+ FrameworkElement? IconElement { get; }
+
+ FrameworkElement? ItemDecorator { get; }
+ }
+
public interface ISidebarItemModel : INotifyPropertyChanged
{
+
///
/// The children of this item that will be rendered as child elements of the SidebarItem
///
diff --git a/src/Files.App.Controls/Sidebar/SidebarItem.Properties.cs b/src/Files.App.Controls/Sidebar/SidebarItem.Properties.cs
index 6d6565235a6a..1ea83c56edc9 100644
--- a/src/Files.App.Controls/Sidebar/SidebarItem.Properties.cs
+++ b/src/Files.App.Controls/Sidebar/SidebarItem.Properties.cs
@@ -83,7 +83,15 @@ public ISidebarItemModel? Item
set { SetValue(ItemProperty, value); }
}
public static readonly DependencyProperty ItemProperty =
- DependencyProperty.Register(nameof(Item), typeof(ISidebarItemModel), typeof(SidebarItem), new PropertyMetadata(null));
+ DependencyProperty.Register(nameof(Item), typeof(ISidebarItemModel), typeof(SidebarItem), new PropertyMetadata(null, OnPropertyChanged));
+
+ public bool UseItemPresentation
+ {
+ get { return (bool)GetValue(UseItemPresentationProperty); }
+ set { SetValue(UseItemPresentationProperty, value); }
+ }
+ public static readonly DependencyProperty UseItemPresentationProperty =
+ DependencyProperty.Register(nameof(UseItemPresentation), typeof(bool), typeof(SidebarItem), new PropertyMetadata(false, OnPropertyChanged));
public bool UseReorderDrop
{
@@ -136,12 +144,18 @@ public static void OnPropertyChanged(DependencyObject sender, DependencyProperty
}
else if (e.Property == IsExpandedProperty)
{
+ if (item.UseItemPresentation && item.Item is { } model && model.IsExpanded != item.IsExpanded)
+ model.IsExpanded = item.IsExpanded;
item.UpdateExpansionState();
}
else if (e.Property == ItemProperty)
{
item.HandleItemChange();
}
+ else if (e.Property == UseItemPresentationProperty && item.UseItemPresentation)
+ {
+ item.UpdateItemPresentation();
+ }
else
{
Debug.Write(e.Property.ToString());
diff --git a/src/Files.App.Controls/Sidebar/SidebarItem.cs b/src/Files.App.Controls/Sidebar/SidebarItem.cs
index 11ceb259ad43..4eee8b2c6dd5 100644
--- a/src/Files.App.Controls/Sidebar/SidebarItem.cs
+++ b/src/Files.App.Controls/Sidebar/SidebarItem.cs
@@ -90,7 +90,10 @@ protected override void OnApplyTemplate()
if (GetTemplateChild("ChevronContainer") is Border chevronContainer)
chevronContainer.PointerPressed += ChevronContainer_PointerPressed;
if (GetTemplateChild("FlyoutChildrenPresenter") is ItemsRepeater flyoutRepeater)
+ {
flyoutRepeater.ElementPrepared += FlyoutChildrenPresenter_ElementPrepared;
+ flyoutRepeater.ItemsSource = Item?.Children;
+ }
}
if (Owner is null)
@@ -124,11 +127,31 @@ private void SidebarItem_Loaded(object sender, RoutedEventArgs e)
public void HandleItemChange()
{
HookupItemChangeListener(null, Item);
+ if (UseItemPresentation)
+ UpdateItemPresentation();
+ UpdateFlyoutChildrenSource();
UpdateExpansionState();
ReevaluateSelection();
CanDrag = Item?.Path is string path && Path.IsPathRooted(path);
}
+ private void UpdateItemPresentation()
+ {
+ var presentation = Item as ISidebarItemPresentationModel;
+ AutomationProperties.SetAutomationId(this, presentation?.Text ?? string.Empty);
+ Text = presentation?.Text;
+ ToolTip = presentation?.ToolTip;
+ Icon = presentation?.IconElement;
+ Decorator = presentation?.ItemDecorator;
+ IsExpanded = Item?.IsExpanded ?? true;
+ }
+
+ private void UpdateFlyoutChildrenSource()
+ {
+ if (GetTemplateChild("FlyoutChildrenPresenter") is ItemsRepeater flyoutRepeater)
+ flyoutRepeater.ItemsSource = Item?.Children;
+ }
+
private void HookupOwners()
{
// Owner is pushed in by the hosting SidebarView's MenuItemsHost_ElementPrepared (top-level rows) or the parent SidebarItem's FlyoutChildrenPresenter_ElementPrepared (flyout children) before Loaded fires. Static SidebarItems declared directly in XAML (MainPage's SettingsButton in SidebarView.Footer) aren't realized through either path, so resolve Owner via a visual-tree walk for them. OwnerExpansionSupport state is applied by OnOwnerChanged.
@@ -181,9 +204,46 @@ private void Item_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
+ case null:
+ case "":
+ if (UseItemPresentation)
+ UpdateItemPresentation();
+ UpdateFlyoutChildrenSource();
+ UpdateExpansionState();
+ ReevaluateSelection();
+ break;
+ case nameof(ISidebarItemPresentationModel.Text):
+ if (UseItemPresentation)
+ {
+ var presentation = Item as ISidebarItemPresentationModel;
+ Text = presentation?.Text;
+ AutomationProperties.SetAutomationId(this, presentation?.Text ?? string.Empty);
+ }
+ break;
+ case nameof(ISidebarItemPresentationModel.ToolTip):
+ if (UseItemPresentation)
+ ToolTip = (Item as ISidebarItemPresentationModel)?.ToolTip;
+ break;
+ case nameof(ISidebarItemPresentationModel.IconElement):
+ if (UseItemPresentation)
+ Icon = (Item as ISidebarItemPresentationModel)?.IconElement;
+ break;
+ case nameof(ISidebarItemPresentationModel.ItemDecorator):
+ if (UseItemPresentation)
+ Decorator = (Item as ISidebarItemPresentationModel)?.ItemDecorator;
+ break;
+ case nameof(ISidebarItemModel.IsExpanded):
+ if (UseItemPresentation)
+ IsExpanded = Item?.IsExpanded ?? true;
+ UpdateExpansionState();
+ break;
case nameof(ISidebarItemModel.HasUnrealizedChildren):
case nameof(ISidebarItemModel.IsLeafWithChildren):
+ UpdateExpansionState();
+ ReevaluateSelection();
+ break;
case nameof(ISidebarItemModel.Children):
+ UpdateFlyoutChildrenSource();
UpdateExpansionState();
ReevaluateSelection();
break;
diff --git a/src/Files.App.Controls/Sidebar/SidebarStyles.xaml b/src/Files.App.Controls/Sidebar/SidebarStyles.xaml
index 004f6683cf04..b3ab9b44b1e8 100644
--- a/src/Files.App.Controls/Sidebar/SidebarStyles.xaml
+++ b/src/Files.App.Controls/Sidebar/SidebarStyles.xaml
@@ -36,20 +36,6 @@
-
-
-
-