Skip to content

Instantly share code, notes, and snippets.

@CheetahChrome
Last active November 12, 2021 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CheetahChrome/1a742384c2ead952972c9d98acd6fc67 to your computer and use it in GitHub Desktop.
Save CheetahChrome/1a742384c2ead952972c9d98acd6fc67 to your computer and use it in GitHub Desktop.
WPF Menu and Submenu Styling as Blue Gradient Background
<!--Specifies the blue Gradient Background -->
<Menu.Resources>
<LinearGradientBrush x:Key="MenuBackroundBrush"
EndPoint="0,1"
StartPoint="0,0">
<GradientStop Color="#FF3A60AD"
Offset="0.528" />
<GradientStop Color="#FF6A85D8"
Offset="0.01" />
<GradientStop Color="#FF3464C4"
Offset="1" />
<GradientStop Color="#FF202E7E"
Offset="1" />
</LinearGradientBrush>
<Style TargetType="Menu">
<Setter Property="Background"
Value="{StaticResource MenuBackroundBrush}" />
</Style>
<Style TargetType="MenuItem">
<Setter Property="Background"
Value="{StaticResource MenuBackroundBrush}" />
</Style>
</Menu.Resources>
<!-- Elevates the whole menu outside of the gradient work -->
<Menu.BitmapEffect>
<DropShadowBitmapEffect />
</Menu.BitmapEffect>
<!--Example to have submenus bound-->
<MenuItem Header="Recent"
Foreground="GhostWhite"
FontSize="16"
Margin="6"
ItemsSource="{Binding Path=RecentJsons}"
Click="SelectMRU"
FontWeight="Bold">
<MenuItem.Icon>
<Image Source="/Assets/history.png" />
</MenuItem.Icon>
</MenuItem>
<!--Handle the gray Popup background for submenus-->
<MenuItem.Template>
<ControlTemplate TargetType="MenuItem">
<Border Name="Border">
<Grid>
<ContentPresenter Margin="6,3,6,3"
ContentSource="Header"
RecognizesAccessKey="True" />
<Popup Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsSubmenuOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Fade">
<Border Name="SubmenuBorder"
SnapsToDevicePixels="True"
Background="LightBlue"
BorderBrush="Transparent"
BorderThickness="1">
<StackPanel IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Cycle" />
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation"
Value="true">
<Setter TargetName="Popup"
Property="PopupAnimation"
Value="None" />
</Trigger>
<Trigger Property="IsHighlighted"
Value="true">
<Setter TargetName="Border"
Property="Background"
Value="LightBlue" />
<Setter TargetName="Border"
Property="BorderBrush"
Value="Transparent" />
<Setter Property="Foreground"
Value="Firebrick" />
</Trigger>
<Trigger Property="IsHighlighted"
Value="False">
<Setter Property="Foreground"
Value="Black" />
</Trigger>
<Trigger SourceName="Popup"
Property="Popup.AllowsTransparency"
Value="True">
<Setter TargetName="SubmenuBorder"
Property="CornerRadius"
Value="0,0,4,4" />
<Setter TargetName="SubmenuBorder"
Property="Padding"
Value="0,0,0,3" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Foreground"
Value="DarkGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</MenuItem.Template>
<!-- Nothing special for each MenuItem, the below has an image on the left and Orange text -->
<MenuItem Header="Clipboard Load"
Foreground="Orange"
FontSize="16"
Margin="6"
ToolTip="Load JSON From Clipboard"
Command="{Binding JSONLoadFromClipboard}"
FontWeight="Bold">
<MenuItem.Icon>
<!-- Remember to make the png file as a `Resource` to the solution, so its embedded in the exe -->
<Image Source="/Assets/clipboard-arrow-down-outline.png" />
</MenuItem.Icon>
</MenuItem
<!-- Example Menu top level template. -->
<Menu Height="Auto"
Name="menu1"
Grid.ColumnSpan="2"
Margin="20, 8, 20, 8"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Foreground="Transparent"
FontSize="16"
FontWeight="Bold"
BorderThickness="2"
>
<!-- Fill in other items within the Gist here -->
</Menu>
public class MRU
{
public string Name { get; set; }
public string Address { get; set; }
public string Data { get; set; }
public bool IsValid => !string.IsNullOrWhiteSpace(Name);
public bool IsFile => !string.IsNullOrWhiteSpace(Address);
public bool IsData => !IsFile;
public MRU(string address)
{
if (string.IsNullOrWhiteSpace(address)
|| !File.Exists(address)) return;
Address = address;
Name = System.IO.Path.GetFileName(address);
}
public override string ToString() => Name;
}
// Install-Package Ben.Demystifier -Version 0.4.1
// The VM's Error is a notified string. This example
// can use the visual error box (placed hidden at the bottom of the main page until an error is in the error property)
// - Error box example at https://gist.github.com/CheetahChrome/e21884ab06e85e27cb2c950ea0b01c60
private void SelectMRU(object sender, RoutedEventArgs e)
{
try
{
var mru = (e.OriginalSource as MenuItem).DataContext as MRU;
if (mru is not null)
{
VM.ClearAll();
this.Title = mru.Name;
ShowJSON(File.ReadAllText(mru.Address));
}
}
catch (Exception ex)
{
VM.Error = ex.Demystify().ToString();
}
}
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Windows.Input;
using Microsoft.Expression.Interactivity.Core;
public class MainVM : INotifiedPropertyChanged
{
private ObservableCollection<MRU> _MRUS;
public ObservableCollection<MRU> MRUS
{
get { return _MRUS; }
set { _MRUS = value; OnPropertyChanged(nameof(MRUS)); }
}
// One can optionally use the visual error box (placed hidden at the bottom of the main page until an error is in the error property)
// - Error box example at https://gist.github.com/CheetahChrome/e21884ab06e85e27cb2c950ea0b01c60
private string _Error;
public string Error
{
get => _Error;
set { _Error = value; OnPropertyChanged(nameof(Error)); OnPropertyChanged(nameof(HasError)); }
}
public bool HasError => !string.IsNullOrEmpty(Error);
public VM()
{
MRUS = new ObservableCollection<MRU>(GetMRUS());
}
public List<MRU> GetMRUS()
{
try
{
var mruText = Properties.Settings.Default.MRUS;
return string.IsNullOrWhiteSpace(mruText) ? new List<MRU>()
: JsonSerializer.Deserialize<List<MRU>>(mruText);
}
catch (Exception ex)
{
Error = ex.ToStringDemystified();
}
return null;
}
// Add a user `Setting` (in the project properties) of `MRUS` of type `string` as `User` setting.
public void AddMRUToUserSettings(MRU mRU)
{
try
{
MRUS.Add(mRU);
var asList = MRUS.ToList<MRU>();
Properties.Settings.Default.MRUS = JsonSerializer.Serialize(asList);
Properties.Settings.Default.Save();
}
catch (Exception ex)
{
Error = ex.Demystify().ToString();
}
}
/// <summary>
/// Event raised when a property changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the property that has changed.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
@CheetahChrome
Copy link
Author

CheetahChrome commented Nov 7, 2021

This WPF based Gist creates a menu bar with an MRU sub list which is saved to the User Settings by Json serialization which looks like this:

SO-013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment