Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

namespace Wpf.Ui.Gallery.ViewModels.Pages.Collections;

public sealed partial class ListViewScrollViewModel : ViewModel
{
[ObservableProperty]
private ObservableCollection<string> _bigArray = GenerateBigArray();

private static ObservableCollection<string> GenerateBigArray()
{
var result = new ObservableCollection<string>();
for (int i = 0; i < 500; i++)
{
result.Add($"Item {i + 1}");
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ public int ListViewSelectionModeComboBoxSelectedIndex
[ObservableProperty]
private ObservableCollection<Person> _basicListViewItems = GeneratePersons();

[ObservableProperty]
private ObservableCollection<string> _bigArray = GenerateBigArray();

private static ObservableCollection<string> GenerateBigArray()
{
var result = new ObservableCollection<string>();
for (int i = 0; i < 500; i++)
{
result.Add($"Item {i + 1}");
}
return result;
}

private static ObservableCollection<Person> GeneratePersons()
{
var random = new Random();
Expand Down
26 changes: 22 additions & 4 deletions src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public partial class MainWindowViewModel(IStringLocalizer<Translations> localize
new NavigationViewItem(nameof(ThumbRate), typeof(ThumbRatePage)),
new NavigationViewItem(nameof(SplitButton), typeof(SplitButtonPage)),
new NavigationViewItem(nameof(Slider), typeof(SliderPage)),
},
}
},
new NavigationViewItem
{
Expand All @@ -75,6 +75,7 @@ public partial class MainWindowViewModel(IStringLocalizer<Translations> localize
new NavigationViewItem(nameof(System.Windows.Controls.DataGrid), typeof(DataGridPage)),
new NavigationViewItem(nameof(ListBox), typeof(ListBoxPage)),
new NavigationViewItem(nameof(Ui.Controls.ListView), typeof(ListViewPage)),
new NavigationViewItem("ListViewNoPageScroll", typeof(ListViewScrollPage)),
new NavigationViewItem(nameof(TreeView), typeof(TreeViewPage)),
#if DEBUG
new NavigationViewItem("TreeList", typeof(TreeListPage)),
Expand Down Expand Up @@ -182,9 +183,26 @@ public partial class MainWindowViewModel(IStringLocalizer<Translations> localize
];

[ObservableProperty]
private ObservableCollection<Wpf.Ui.Controls.MenuItem> _trayMenuItems =
private ObservableCollection<object> _trayMenuItems =
[
new Wpf.Ui.Controls.MenuItem { Header = "Home", Tag = "tray_home" },
new Wpf.Ui.Controls.MenuItem { Header = "Close", Tag = "tray_close" },
new Wpf.Ui.Controls.MenuItem()
{
Header = "Home",
Tag = "tray_home",
Icon = new SymbolIcon { Symbol = SymbolRegular.Home24 }
},
new Wpf.Ui.Controls.MenuItem()
{
Header = "Settings",
Tag = "tray_settings",
Icon = new SymbolIcon { Symbol = SymbolRegular.Settings24 }
},
new Separator(),
new Wpf.Ui.Controls.MenuItem()
{
Header = "Close",
Tag = "tray_close",
Icon = new SymbolIcon { Symbol = SymbolRegular.Dismiss24 }
},
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Page x:Class="Wpf.Ui.Gallery.Views.Pages.Collections.ListViewScrollPage"
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:Wpf.Ui.Gallery.Views.Pages.Collections"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:controls="clr-namespace:Wpf.Ui.Gallery.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="ListViewScrollPage"
d:DataContext="{d:DesignInstance local:ListViewPage,
IsDesignTimeCreatable=False}"
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
ui:NavigationView.IsScrollable="False">

<ui:Card VerticalAlignment="Top" VerticalContentAlignment="Stretch" Padding="14" Margin="0,0,0,24">
<ui:ListView ItemsSource="{Binding ViewModel.BigArray, Mode=OneWay}"
d:ItemsSource="{d:SampleData ItemCount=10}"
BorderThickness="0" SelectionMode="Single"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
TextOptions.TextFormattingMode="Display"
TextOptions.TextRenderingMode="ClearType"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
RenderOptions.ClearTypeHint="Enabled">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type system:String}">
<ui:TextBlock FontSize="12" VerticalAlignment="Top"
Text="{Binding}"
FontFamily="Consolas, Cascadia Mono, Segoe UI Mono, Courier New"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ui:ListView>
</ui:Card>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using System.Windows;
using System.Windows.Controls;
using Wpf.Ui.Controls;
using Wpf.Ui.Gallery.ControlsLookup;
using Wpf.Ui.Gallery.ViewModels.Pages.Collections;

namespace Wpf.Ui.Gallery.Views.Pages.Collections;
/// <summary>
/// Interaction logic for ListViewScrollPage.xaml
/// </summary>
[GalleryPage("Selectable list.", SymbolRegular.GroupList24)]
public partial class ListViewScrollPage : INavigableView<ListViewScrollViewModel>
{
/// <inheritdoc />
public ListViewScrollViewModel ViewModel { get; set; }

public ListViewScrollPage(ListViewScrollViewModel viewModel)
{
ViewModel = viewModel;
DataContext = this;

InitializeComponent();
}

}
93 changes: 93 additions & 0 deletions src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ IContentDialogService contentDialogService
snackbarService.SetSnackbarPresenter(SnackbarPresenter);
navigationService.SetNavigationControl(NavigationView);
contentDialogService.SetDialogHost(RootContentDialog);
SetupTrayMenuEvents();
}

public MainWindowViewModel ViewModel { get; }
Expand All @@ -38,6 +39,98 @@ IContentDialogService contentDialogService

private bool _isPaneOpenedOrClosedFromCode;

private void SetupTrayMenuEvents()
{
foreach (var menuItem in ViewModel.TrayMenuItems)
{
if (menuItem is MenuItem item)
{
item.Click += OnTrayMenuItemClick;
}
}
}

private void OnTrayMenuItemClick(object sender, RoutedEventArgs e)
{
if (sender is not Wpf.Ui.Controls.MenuItem menuItem)
{
return;
}

var tag = menuItem.Tag?.ToString() ?? string.Empty;

Debug.WriteLine($"System Tray Click: {menuItem.Header}, Tag: {tag}");

switch (tag)
{
case "tray_home":
HandleTrayHomeClick();
break;
case "tray_settings":
HandleTraySettingsClick();
break;
case "tray_close":
HandleTrayCloseClick();
break;
default:
if (!string.IsNullOrEmpty(tag))
{
System.Diagnostics.Debug.WriteLine($"unknown Tag: {tag}");
}

break;
}
}

private void HandleTrayHomeClick()
{
System.Diagnostics.Debug.WriteLine("Tray menu - Home Click");

ShowAndActivateWindow();

NavigateToPage(typeof(DashboardPage));
}

private void HandleTraySettingsClick()
{
System.Diagnostics.Debug.WriteLine("Tray menu - Settings Click");

ShowAndActivateWindow();

NavigateToPage(typeof(SettingsPage));
}

private static void HandleTrayCloseClick()
{
System.Diagnostics.Debug.WriteLine("Tray menu - Close Click");

Application.Current.Shutdown();
}

private void ShowAndActivateWindow()
{
if (WindowState == WindowState.Minimized)
{
SetCurrentValue(WindowStateProperty, WindowState.Normal);
}

Show();
_ = Activate();
_ = Focus();
}

private void NavigateToPage(Type pageType)
{
try
{
NavigationView.Navigate(pageType);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"NavigateToPage {pageType.Name} Error: {ex.Message}");
}
}

private void OnNavigationSelectionChanged(object sender, RoutedEventArgs e)
{
if (sender is not Wpf.Ui.Controls.NavigationView navigationView)
Expand Down
30 changes: 28 additions & 2 deletions src/Wpf.Ui.Tray/Controls/NotifyIcon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ public NotifyIcon()
internalNotifyIconManager = new Wpf.Ui.Tray.Internal.InternalNotifyIconManager();

RegisterHandlers();

// Listen for DataContext changes to update ContextMenu
DataContextChanged += OnDataContextChanged;
}

/// <summary>
Expand Down Expand Up @@ -363,6 +366,9 @@ protected virtual void Dispose(bool disposing)

System.Diagnostics.Debug.WriteLine($"INFO | {typeof(NotifyIcon)} disposed.", "Wpf.Ui.NotifyIcon");

// Clean up event handlers
DataContextChanged -= OnDataContextChanged;

Unregister();

internalNotifyIconManager.Dispose();
Expand All @@ -375,6 +381,13 @@ protected virtual void Dispose(bool disposing)
protected virtual void OnMenuChanged(ContextMenu contextMenu)
{
internalNotifyIconManager.ContextMenu = contextMenu;

// Set the DataContext for ContextMenu to enable binding
if (contextMenu.DataContext == null && DataContext != null)
{
contextMenu.DataContext = DataContext;
}

internalNotifyIconManager.ContextMenu.SetCurrentValue(Control.FontSizeProperty, MenuFontSize);
}

Expand Down Expand Up @@ -410,7 +423,6 @@ private static void OnFocusOnLeftClickChanged(DependencyObject d, DependencyProp
if (e.NewValue is not bool newValue)
{
notifyIcon.FocusOnLeftClick = false;

return;
}

Expand All @@ -427,7 +439,6 @@ private static void OnMenuOnRightClickChanged(DependencyObject d, DependencyProp
if (e.NewValue is not bool newValue)
{
notifyIcon.MenuOnRightClick = false;

return;
}

Expand Down Expand Up @@ -455,6 +466,12 @@ private void InitializeIcon()
internalNotifyIconManager.Icon = Icon;
internalNotifyIconManager.MenuOnRightClick = MenuOnRightClick;
internalNotifyIconManager.FocusOnLeftClick = FocusOnLeftClick;

// Add Menu initialization
if (Menu != null)
{
OnMenuChanged(Menu);
}
}

private void RegisterHandlers()
Expand All @@ -466,4 +483,13 @@ private void RegisterHandlers()
internalNotifyIconManager.MiddleClick += OnMiddleClick;
internalNotifyIconManager.MiddleDoubleClick += OnMiddleDoubleClick;
}

private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Update ContextMenu DataContext when NotifyIcon DataContext changes
if (Menu != null && e.NewValue != null)
{
Menu.DataContext = e.NewValue;
}
}
}
5 changes: 4 additions & 1 deletion src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ protected virtual void OpenMenu()

// Without setting the handler window at the front, menu may appear behind the taskbar
_ = Interop.User32.SetForegroundWindow(HookWindow.Handle);
ContextMenuService.SetPlacement(ContextMenu, PlacementMode.MousePoint);

// Set placement properties for better positioning
ContextMenu.SetCurrentValue(ContextMenu.PlacementProperty, PlacementMode.MousePoint);
ContextMenu.SetCurrentValue(ContextMenu.PlacementTargetProperty, null);

// ContextMenu.ApplyMica();
ContextMenu.SetCurrentValue(ContextMenu.IsOpenProperty, true);
Expand Down
Loading