How to load an application to systemtray using Avalonia - c#

How can I load an Avalonia application to the systemtray and set the menu items?

Avalonia seems to be a UI/WPF library/resource, so I don't think that would affect how your application runs. This would be related to WPF application development.
A bit of reading around, and it appears you may need to use the System.Windows.Forms.NotifyIcon.
You would want to instantiate the icon in the context of your main application.
I created a sample WPF application using .NET Framework (so that I was able to reference System.Windows.Forms) and was able to have a system tray icon appear for my application.
Here is some example code:
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : System.Windows.Application
{
NotifyIcon TrayIcon;
public App()
{
// we initialize the tray icon in the application constructor
// and have that reference for the lifetime of the application
TrayIcon = new NotifyIcon()
{
Icon = SystemIcons.Information,
ContextMenu = new ContextMenu(new MenuItem[] { new MenuItem("Show/Hide MyApp", ShowHide), new MenuItem("Exit", OnExit) }),
Visible = true
};
}
private void OnExit(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void ShowHide(object sender, EventArgs e)
{
throw new NotImplementedException();
}
}
It appears Avalonia does offer their own version of a TrayIcon. Here is the class I was able to find in their Source Code:
TrayIcon.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
public sealed class TrayIcons : AvaloniaList<TrayIcon>
{
}
public class TrayIcon : AvaloniaObject, INativeMenuExporterProvider, IDisposable
{
private readonly ITrayIconImpl? _impl;
private ICommand? _command;
private TrayIcon(ITrayIconImpl? impl)
{
if (impl != null)
{
_impl = impl;
_impl.SetIsVisible(IsVisible);
_impl.OnClicked = () =>
{
Clicked?.Invoke(this, EventArgs.Empty);
if (Command?.CanExecute(CommandParameter) == true)
{
Command.Execute(CommandParameter);
}
};
}
}
public TrayIcon() : this(PlatformManager.CreateTrayIcon())
{
}
static TrayIcon()
{
IconsProperty.Changed.Subscribe(args =>
{
if (args.Sender is Application)
{
if (args.OldValue.Value != null)
{
RemoveIcons(args.OldValue.Value);
}
if (args.NewValue.Value != null)
{
args.NewValue.Value.CollectionChanged += Icons_CollectionChanged;
}
}
});
var app = Application.Current ?? throw new InvalidOperationException("Application not yet initialized.");
if (app.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
lifetime.Exit += Lifetime_Exit;
}
}
/// <summary>
/// Raised when the TrayIcon is clicked.
/// Note, this is only supported on Win32 and some Linux DEs,
/// on OS X this event is not raised.
/// </summary>
public event EventHandler? Clicked;
/// <summary>
/// Defines the <see cref="Command"/> property.
/// </summary>
public static readonly DirectProperty<TrayIcon, ICommand?> CommandProperty =
Button.CommandProperty.AddOwner<TrayIcon>(
trayIcon => trayIcon.Command,
(trayIcon, command) => trayIcon.Command = command,
enableDataValidation: true);
/// <summary>
/// Defines the <see cref="CommandParameter"/> property.
/// </summary>
public static readonly StyledProperty<object?> CommandParameterProperty =
Button.CommandParameterProperty.AddOwner<MenuItem>();
/// <summary>
/// Defines the <see cref="TrayIcons"/> attached property.
/// </summary>
public static readonly AttachedProperty<TrayIcons> IconsProperty
= AvaloniaProperty.RegisterAttached<TrayIcon, Application, TrayIcons>("Icons");
/// <summary>
/// Defines the <see cref="Menu"/> property.
/// </summary>
public static readonly StyledProperty<NativeMenu?> MenuProperty
= AvaloniaProperty.Register<TrayIcon, NativeMenu?>(nameof(Menu));
/// <summary>
/// Defines the <see cref="Icon"/> property.
/// </summary>
public static readonly StyledProperty<WindowIcon?> IconProperty =
Window.IconProperty.AddOwner<TrayIcon>();
/// <summary>
/// Defines the <see cref="ToolTipText"/> property.
/// </summary>
public static readonly StyledProperty<string?> ToolTipTextProperty =
AvaloniaProperty.Register<TrayIcon, string?>(nameof(ToolTipText));
/// <summary>
/// Defines the <see cref="IsVisible"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsVisibleProperty =
Visual.IsVisibleProperty.AddOwner<TrayIcon>();
public static void SetIcons(AvaloniaObject o, TrayIcons trayIcons) => o.SetValue(IconsProperty, trayIcons);
public static TrayIcons GetIcons(AvaloniaObject o) => o.GetValue(IconsProperty);
/// <summary>
/// Gets or sets the <see cref="Command"/> property of a TrayIcon.
/// </summary>
public ICommand? Command
{
get => _command;
set => SetAndRaise(CommandProperty, ref _command, value);
}
/// <summary>
/// Gets or sets the parameter to pass to the <see cref="Command"/> property of a
/// <see cref="TrayIcon"/>.
/// </summary>
public object? CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
/// <summary>
/// Gets or sets the Menu of the TrayIcon.
/// </summary>
public NativeMenu? Menu
{
get => GetValue(MenuProperty);
set => SetValue(MenuProperty, value);
}
/// <summary>
/// Gets or sets the icon of the TrayIcon.
/// </summary>
public WindowIcon? Icon
{
get => GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
/// <summary>
/// Gets or sets the tooltip text of the TrayIcon.
/// </summary>
public string? ToolTipText
{
get => GetValue(ToolTipTextProperty);
set => SetValue(ToolTipTextProperty, value);
}
/// <summary>
/// Gets or sets the visibility of the TrayIcon.
/// </summary>
public bool IsVisible
{
get => GetValue(IsVisibleProperty);
set => SetValue(IsVisibleProperty, value);
}
public INativeMenuExporter? NativeMenuExporter => _impl?.MenuExporter;
private static void Lifetime_Exit(object? sender, ControlledApplicationLifetimeExitEventArgs e)
{
var app = Application.Current ?? throw new InvalidOperationException("Application not yet initialized.");
var trayIcons = GetIcons(app);
RemoveIcons(trayIcons);
}
private static void Icons_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.OldItems is not null)
RemoveIcons(e.OldItems.Cast<TrayIcon>());
}
private static void RemoveIcons(IEnumerable<TrayIcon> icons)
{
foreach (var icon in icons)
{
icon.Dispose();
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == IconProperty)
{
_impl?.SetIcon(Icon?.PlatformImpl);
}
else if (change.Property == IsVisibleProperty)
{
_impl?.SetIsVisible(change.GetNewValue<bool>());
}
else if (change.Property == ToolTipTextProperty)
{
_impl?.SetToolTipText(change.GetNewValue<string?>());
}
else if (change.Property == MenuProperty)
{
_impl?.MenuExporter?.SetNativeMenu(change.GetNewValue<NativeMenu?>());
}
}
/// <summary>
/// Disposes the tray icon (removing it from the tray area).
/// </summary>
public void Dispose() => _impl?.Dispose();
}
}
And an application example from them as well which shows an example implementation:
App.xaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ControlCatalog.ViewModels"
x:DataType="vm:ApplicationViewModel"
x:CompileBindings="True"
Name="Avalonia ControlCatalog"
x:Class="ControlCatalog.App">
<Application.Styles>
<Style Selector="TextBlock.h1, TextBlock.h2, TextBlock.h3">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Medium" />
</Style>
<Style Selector="TextBlock.h2">
<Setter Property="FontSize" Value="14" />
</Style>
<Style Selector="TextBlock.h3">
<Setter Property="FontSize" Value="12" />
</Style>
<Style Selector="Label.h1">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Medium" />
</Style>
<Style Selector="Label.h2">
<Setter Property="FontSize" Value="14" />
</Style>
<Style Selector="Label.h3">
<Setter Property="FontSize" Value="12" />
</Style>
<StyleInclude Source="avares://ControlSamples/HamburgerMenu/HamburgerMenu.xaml" />
</Application.Styles>
<TrayIcon.Icons>
<TrayIcons>
<TrayIcon Icon="/Assets/test_icon.ico" ToolTipText="Avalonia Tray Icon ToolTip">
<TrayIcon.Menu>
<NativeMenu>
<NativeMenuItem Header="Settings">
<NativeMenu>
<NativeMenuItem Header="Option 1" ToggleType="Radio" IsChecked="True" Command="{Binding ToggleCommand}" />
<NativeMenuItem Header="Option 2" ToggleType="Radio" IsChecked="True" Command="{Binding ToggleCommand}" />
<NativeMenuItemSeparator />
<NativeMenuItem Header="Option 3" ToggleType="CheckBox" IsChecked="True" Command="{Binding ToggleCommand}" />
<NativeMenuItem Icon="/Assets/test_icon.ico" Header="Restore Defaults" Command="{Binding ToggleCommand}" />
</NativeMenu>
</NativeMenuItem>
<NativeMenuItem Header="Exit" Command="{Binding ExitCommand}" />
</NativeMenu>
</TrayIcon.Menu>
</TrayIcon>
</TrayIcons>
</TrayIcon.Icons>
</Application>
They do have some documentation here.
Unfortunately their documentation website is hard to navigate, and doesn't seem to mention anything regarding "TrayIcon" when using their search feature.
Fortunately, it seems they also have a Customer Support team, and you could always post questions to their support team directly on their GitHub place:
https://github.com/AvaloniaUI/Avalonia/discussions

Related

Why does WPF Loaded event not trigger?

In WPF, I have a DataGrid, and the DataGrid has an attached property (LoadingProperty). When LoadingProperty value is True, I set the DataGrid background with VisualBrush, why does the LoadingCircle UserControl Loaded event not Trigger?
DataGrid Style
<Style TargetType="{x:Type DataGrid}">
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="Foreground" Value="{StaticResource ForegroundLightBrush}" />
<Setter Property="Background" Value="{StaticResource BackgroundLoadingBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource BackgroundDarkBrush}" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="HorizontalGridLinesBrush" Value="{StaticResource BackgroundDarkBrush}" />
<Setter Property="VerticalGridLinesBrush" Value="{StaticResource BackgroundDarkBrush}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="EnableRowVirtualization" Value="True" />
<Setter Property="EnableColumnVirtualization" Value="True" />
<Setter Property="materialDesign:DataGridAssist.CellPadding" Value="16,8,16,8" />
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="{StaticResource ForegroundLightBrush}" Text="No record found" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="local:LoadingProperty.Value" Value="True">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<local:LoadingCircle Width="50" Height="50" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
LoadingProperty Code
public class LoadingProperty : BaseAttachedProperty<LoadingProperty, bool>
{
}
BaseAttachedProperty Code
using System;
using System.Windows;
namespace TransportsSys.App
{
/// <summary>
/// A base attached property to replace the vanilla WPF attached property
/// </summary>
/// <typeparam name="Parent">The parent class to be the attached property</typeparam>
/// <typeparam name="Property">The type of this attached property</typeparam>
public abstract class BaseAttachedProperty<Parent, Property>
where Parent : new()
{
#region Public Events
/// <summary>
/// Fired when the value changes
/// </summary>
public event Action<DependencyObject, DependencyPropertyChangedEventArgs> ValueChanged = (sender, e) => { };
/// <summary>
/// Fired when the value changes, even when the value is the same
/// </summary>
public event Action<DependencyObject, object> ValueUpdated = (sender, value) => { };
#endregion
#region Public Properties
/// <summary>
/// A singleton instance of our parent class
/// </summary>
public static Parent Instance { get; private set; } = new Parent();
#endregion
#region Attached Property Definitions
/// <summary>
/// The attached property for this class
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
"Value",
typeof(Property),
typeof(BaseAttachedProperty<Parent, Property>),
new UIPropertyMetadata(
default(Property),
new PropertyChangedCallback(OnValuePropertyChanged),
new CoerceValueCallback(OnValuePropertyUpdated)
));
/// <summary>
/// The callback event when the <see cref="ValueProperty"/> is changed
/// </summary>
/// <param name="d">The UI element that had it's property changed</param>
/// <param name="e">The arguments for the event</param>
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Call the parent function
(Instance as BaseAttachedProperty<Parent, Property>)?.OnValueChanged(d, e);
// Call event listeners
(Instance as BaseAttachedProperty<Parent, Property>)?.ValueChanged(d, e);
}
/// <summary>
/// The callback event when the <see cref="ValueProperty"/> is changed, even if it is the same value
/// </summary>
/// <param name="d">The UI element that had it's property changed</param>
/// <param name="e">The arguments for the event</param>
private static object OnValuePropertyUpdated(DependencyObject d, object value)
{
// Call the parent function
(Instance as BaseAttachedProperty<Parent, Property>)?.OnValueUpdated(d, value);
// Call event listeners
(Instance as BaseAttachedProperty<Parent, Property>)?.ValueUpdated(d, value);
// Return the value
return value;
}
/// <summary>
/// Gets the attached property
/// </summary>
/// <param name="d">The element to get the property from</param>
/// <returns></returns>
public static Property GetValue(DependencyObject d) => (Property)d.GetValue(ValueProperty);
/// <summary>
/// Sets the attached property
/// </summary>
/// <param name="d">The element to get the property from</param>
/// <param name="value">The value to set the property to</param>
public static void SetValue(DependencyObject d, Property value) => d.SetValue(ValueProperty, value);
#endregion
#region Event Methods
/// <summary>
/// The method that is called when any attached property of this type is changed
/// </summary>
/// <param name="sender">The UI element that this property was changed for</param>
/// <param name="e">The arguments for this event</param>
public virtual void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { }
/// <summary>
/// The method that is called when any attached property of this type is changed, even if the value is the same
/// </summary>
/// <param name="sender">The UI element that this property was changed for</param>
/// <param name="e">The arguments for this event</param>
public virtual void OnValueUpdated(DependencyObject sender, object value) { }
#endregion
}
}
LoadingCircle UserControl Code
// LoadingCircle.xmal
<UserControl
x:Class="TransportsSys.App.LoadingCircle"
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"
mc:Ignorable="d">
<Grid>
<TextBlock FontSize="{StaticResource FontSizeXXXXLarge}" Foreground="{StaticResource ForegroundLightBrush}" Style="{StaticResource DataGridSpinningText}" />
</Grid>
</UserControl>
// LoadingCircle.xmal.cs
using System.Windows.Controls;
namespace TransportsSys.App
{
/// <summary>
/// LoadingCircle.xaml 的交互逻辑
/// </summary>
public partial class LoadingCircle : UserControl
{
public LoadingCircle()
{
InitializeComponent();
Loaded += (s, e) =>
{
// can not trigger
};
}
}
}
I wonder why that is. When I use LoadingCircle alone in a page, its Loaded event fires normally.
This looks like a WPF bug.
A workaround is to put the VisualBrush in resources and use it in the trigger:
<UserControl.Resources>
<VisualBrush x:Key="Brush" Stretch="None">
<VisualBrush.Visual>
<local:LoadingCircle Width="50" Height="50" />
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
...
<Trigger Property="local:LoadingProperty.Value" Value="True">
<Setter Property="Background" Value="{StaticResource Brush}" />
</Trigger>
A MRE is available here.

Maintaining Label Position in Front of Image (stretch mode is uniform) Regardless of Resizing of the Window WPF

What i'm trying to do is having an image that describe something with content in and there is an indicators in the image (the image is already like this)
ex: enter image description here
And my labels will cover these yellow parts
and after placing them, the should stay covering the same part regardless of the window size.
I have tried various things, starting from this article. and after many tries here is my current code, the problem in it is:
1- It should be already follow the same position but i don't find it working.
2- I don't think my approach is the right way to do this, i'm certain there should be an easier way.
First: Window1.xaml
<Window.Resources>
<!--
A data-template that defines the visuals for a rectangle.
-->
<DataTemplate
DataType="{x:Type local:RectangleViewModel}"
>
<Grid>
<Thumb
Width="20"
Height="20"
DragDelta="Thumb_DragDelta"
>
<Thumb.Template>
<ControlTemplate>
<Border
Background="Blue"
Cursor="Hand"
>
<Viewbox>
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ImageWidth ,FallbackValue=1}" />
</Viewbox>
</Border>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Grid>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<!--
Initialise the view model that supplies the UI with data.
-->
<local:ViewModel />
</Window.DataContext>
<Grid Background="Aqua">
<Image Source="pack://application:,,,/Images/1111.png" SizeChanged="Image_SizeChanged" Stretch="Uniform"/>
<!--
This ItemsControl presents the colored rectangles.
The data-template that defines the visuals for each rectangle is in the
resources section at the start of this file.
-->
<ItemsControl
ItemsSource="{Binding Rectangles}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter
Property="Canvas.Left"
Value="{Binding X}"
/>
<Setter
Property="Canvas.Top"
Value="{Binding Y}"
/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
Second: Window1.xaml.cs
using System.Linq;
using System.Windows;
using System.Windows.Controls.Primitives;
namespace SampleCode
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
SizeChanged += Window1_SizeChanged;
}
/// <summary>
/// Handle the resize of the window
/// </summary>
private void Window1_SizeChanged(object sender, SizeChangedEventArgs e)
{
Size oldSize = e.PreviousSize;
Size newSize = e.NewSize;
((ViewModel)this.DataContext).Rectangles.ToList().ForEach(i => i.update(newSize));
}
/// <summary>
/// Hundle the resize of the window
/// </summary>
private void Image_SizeChanged(object sender, SizeChangedEventArgs e)
{
((ViewModel)this.DataContext).Rectangles.ToList().ForEach(i => i.ImageSize = e.NewSize);
}
/// <summary>
/// Handle the user dragging the rectangle.
/// </summary>
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Thumb thumb = (Thumb)sender;
RectangleViewModel myRectangle = (RectangleViewModel)thumb.DataContext;
//
// Update the the position of the rectangle in the view-model.
//
myRectangle.X += e.HorizontalChange;
myRectangle.Y += e.VerticalChange;
}
}
}
Third: ViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace SampleCode
{
/// <summary>
/// A simple example of a view-model.
/// </summary>
public class ViewModel : INotifyPropertyChanged
{
#region Data Members
/// <summary>
/// The list of rectangles that is displayed in the ListBox.
/// </summary>
private ObservableCollection<RectangleViewModel> rectangles = new ObservableCollection<RectangleViewModel>();
/// <summary>
/// The image size
/// </summary>
private Size _imageSize ;
#endregion Data Members
public Size ImageSize
{
get => _imageSize; set
{
{
if (_imageSize == value)
{
return;
}
_imageSize = value;
OnPropertyChanged("ImageWidth");
};
}
}
public ViewModel()
{
// Populate the view model with some example data.
var r1 = new RectangleViewModel();
rectangles.Add(r1);
}
/// <summary>
/// The list of rectangles that is displayed in the ListBox.
/// </summary>
public ObservableCollection<RectangleViewModel> Rectangles
{
get
{
return rectangles;
}
}
#region INotifyPropertyChanged Members
/// <summary>
/// Raises the 'PropertyChanged' event when the value of a property of the view model has changed.
/// </summary>
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// 'PropertyChanged' event that is raised when the value of a property of the view model has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
Forth: RectangleViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Media;
using System.Windows;
namespace SampleCode
{
/// <summary>
/// Defines the view-model for a simple displayable rectangle.
/// </summary>
public class RectangleViewModel : INotifyPropertyChanged
{
#region Data Members
/// <summary>
/// The X coordinate of the location of the rectangle (in content coordinates).
/// </summary>
private double x;
/// <summary>
/// The Y coordinate of the location of the rectangle (in content coordinates).
/// </summary>
private double y;
/// <summary>
/// The size of the current window
/// </summary>
public Size mCurrentWindwoSize;
#endregion Data Members
public RectangleViewModel()
{
}
/// <summary>
/// The size of the background image
/// </summary>
public Size ImageSize { get; set; }
/// <summary>
/// The X coordinate of the location of the rectangle (in content coordinates).
/// </summary>
public double X
{
get
{
return (mCurrentWindwoSize.Width-ImageSize.Width)/2 + Data.x * ImageSize.Width;
}
set
{
if ((mCurrentWindwoSize.Width - ImageSize.Width) / 2 + Data.x * ImageSize.Width == value)return;
Data.x =( value - (mCurrentWindwoSize.Width - ImageSize.Width) / 2 )/ ImageSize.Width;
OnPropertyChanged("X");
}
}
/// <summary>
/// The Y coordinate of the location of the rectangle (in content coordinates).
/// </summary>
public double Y
{
get
{
return (mCurrentWindwoSize.Height - ImageSize.Height) / 2 + Data.y * ImageSize.Height;
}
set
{
if ((mCurrentWindwoSize.Height - ImageSize.Height) / 2 + Data.y * ImageSize.Height == value) return;
Data.y = (value - (mCurrentWindwoSize.Height - ImageSize.Height) / 2) / ImageSize.Height;
OnPropertyChanged("Y");
}
}
public void update(Size size)
{
mCurrentWindwoSize = size;
OnPropertyChanged("X");
OnPropertyChanged("Y");
}
#region INotifyPropertyChanged Members
/// <summary>
/// Raises the 'PropertyChanged' event when the value of a property of the view model has changed.
/// </summary>
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// 'PropertyChanged' event that is raised when the value of a property of the view model has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
Fifth: Data.cs
namespace SampleCode
{
public static class Data
{
public static double x = 0.5;//default
public static double y = 0.5;//default
}
}
Thanks in advance.
I finally managed to do it,
The main issue was that i forget that the windows upper panel and border are included in ActualWidth and ActualHeight, and after some fixes here is the final code:
First: Window1.xaml
<Window.Resources>
<!--
A data-template that defines the visuals for a rectangle.
-->
<DataTemplate
DataType="{x:Type local:RectangleViewModel}"
>
<Grid>
<Thumb
Width="{Binding LabelWidth}"
Height="{Binding LabelWidth}"
DragDelta="Thumb_DragDelta"
>
<Thumb.Template>
<ControlTemplate>
<Border
Background="Blue"
Cursor="Hand"
>
<Viewbox>
<TextBlock Text="1" />
</Viewbox>
</Border>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Grid>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<!--
Initialise the view model that supplies the UI with data.
-->
<local:ViewModel />
</Window.DataContext>
<Grid Background="Aqua">
<Image Source="pack://application:,,,/Images/1112.png" Stretch="Uniform"/>
<!--
This ItemsControl presents the colored rectangles.
The data-template that defines the visuals for each rectangle is in the
resources section at the start of this file.
-->
<ItemsControl
ItemsSource="{Binding Rectangles}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas SizeChanged="Canvas_SizeChanged"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter
Property="Canvas.Left"
Value="{Binding X}"
/>
<Setter
Property="Canvas.Top"
Value="{Binding Y}"
/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
Second: Window1.xaml.cs
using System.Linq;
using System.Windows;
using System.Windows.Controls.Primitives;
namespace SampleCode
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
/// <summary>
/// Handle the user dragging the rectangle.
/// </summary>
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Thumb thumb = (Thumb)sender;
RectangleViewModel myRectangle = (RectangleViewModel)thumb.DataContext;
//
// Update the the position of the rectangle in the view-model.
//
myRectangle.X += e.HorizontalChange;
myRectangle.Y += e.VerticalChange;
}
/// <summary>
/// Hundle the resize of the canvas
/// </summary>
private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
Size newSize = e.NewSize;
((ViewModel)this.DataContext).Rectangles.ToList().ForEach(i => i.update(e.NewSize));
}
}
}
Third: ViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace SampleCode
{
/// <summary>
/// A simple example of a view-model.
/// </summary>
public class ViewModel : INotifyPropertyChanged
{
#region Data Members
/// <summary>
/// The list of rectangles that is displayed in the ListBox.
/// </summary>
private ObservableCollection<RectangleViewModel> rectangles = new ObservableCollection<RectangleViewModel>();
#endregion
public ViewModel()
{
// Populate the view model with some example data.
var r1 = new RectangleViewModel(0.1,0.3);
rectangles.Add(r1);
var r2 = new RectangleViewModel(0.2,0.4);
rectangles.Add(r2);
}
/// <summary>
/// The list of rectangles that is displayed in the ListBox.
/// </summary>
public ObservableCollection<RectangleViewModel> Rectangles
{
get
{
return rectangles;
}
}
#region INotifyPropertyChanged Members
/// <summary>
/// Raises the 'PropertyChanged' event when the value of a property of the view model has changed.
/// </summary>
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// 'PropertyChanged' event that is raised when the value of a property of the view model has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
and Finally, Forth: RectabgleViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Media;
using System.Windows;
namespace SampleCode
{
/// <summary>
/// Defines the view-model for a simple displayable rectangle.
/// </summary>
public class RectangleViewModel : INotifyPropertyChanged
{
#region Data Members
/// <summary>
/// The aspect ration of the image
/// </summary>
private double ratio = 1.375;
/// <summary>
/// The label width
/// </summary>
private double mLabelWidth = 20;
/// <summary>
/// The X coordinate of the location of the rectangle (in content coordinates).
/// </summary>
private double mX;
/// <summary>
/// The Y coordinate of the location of the rectangle (in content coordinates).
/// </summary>
private double mY;
/// <summary>
/// The size of the current window
/// </summary>
public Size mCurrentWindwoSize = new Size(450,300);
/// <summary>
/// The size of the label as a percantage from the image size
/// </summary>
private readonly double sizePercentage = 0.05;
#endregion Data Members
public RectangleViewModel(double x, double y)
{
mX = x;
mY = y;
}
/// <summary>
/// The size of the background image
/// </summary>
public Size ImageSize { get; set; }
/// <summary>
/// The width of the label
/// </summary>
public double LabelWidth
{
get { return mLabelWidth; }
set { if (value == mLabelWidth)
return;
else
mLabelWidth = value;
OnPropertyChanged("LabelWidth");
}
}
/// <summary>
/// The X coordinate of the location of the rectangle (in content coordinates).
/// </summary>
public double X
{
get
{
return (mCurrentWindwoSize.Width-ImageSize.Width)/2 + mX * ImageSize.Width - mLabelWidth/2;
}
set
{
//if ((mCurrentWindwoSize.Width - ImageSize.Width) / 2 + mX * ImageSize.Width == value)return;
mX =( value - (mCurrentWindwoSize.Width - ImageSize.Width) / 2 + mLabelWidth/2)/ ImageSize.Width;
OnPropertyChanged("X");
}
}
/// <summary>
/// The Y coordinate of the location of the rectangle (in content coordinates).
/// </summary>
public double Y
{
get
{
return (mCurrentWindwoSize.Height - ImageSize.Height) / 2 + mY * ImageSize.Height - mLabelWidth/2 ;
}
set
{
//if ((mCurrentWindwoSize.Height - ImageSize.Height) / 2 + mY * ImageSize.Height == value) return;
mY = (value - (mCurrentWindwoSize.Height - ImageSize.Height) / 2 + mLabelWidth/2) / ImageSize.Height;
OnPropertyChanged("Y");
}
}
public void update(Size windowSize)
{
mCurrentWindwoSize = windowSize;
if (windowSize.Height > windowSize.Width * ratio)
{
ImageSize = new Size(windowSize.Width, windowSize.Width * ratio);
}
else
{
ImageSize = new Size(windowSize.Height / ratio, windowSize.Height);
}
LabelWidth = ImageSize.Width * sizePercentage;
X = (mCurrentWindwoSize.Width - ImageSize.Width) / 2 + mX * ImageSize.Width - mLabelWidth/2;
Y = (mCurrentWindwoSize.Height - ImageSize.Height) / 2 + mY * ImageSize.Height - mLabelWidth/2;
}
#region INotifyPropertyChanged Members
/// <summary>
/// Raises the 'PropertyChanged' event when the value of a property of the view model has changed.
/// </summary>
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// 'PropertyChanged' event that is raised when the value of a property of the view model has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
I think that it still needs some clean up but at least it do the job required by it.
I will try to make it cleaner while moving it to the actual project.
Thanks.

How to get value of a property of the WPF ListBoxItem

Hello I am trying to set the value of a property of my ListBoxItem, just not sure how to use Binding in this case if someone could help me, I appreciate it since!
Below XAML
<ControlTemplate TargetType="controls:ModernVerticalMenu">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{TemplateBinding ListWidth}"/>
<ColumnDefinition Width="{TemplateBinding ListWidth}"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Background="{TemplateBinding BackColor}" Height="{TemplateBinding Height}" BorderThickness="1" BorderBrush="{DynamicResource bordaSuperior}">
<!-- link list -->
<ListBox x:Name="LinkList" ItemsSource="{Binding Links, RelativeSource={RelativeSource TemplatedParent}}"
ScrollViewer.HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="50" Background="Transparent" Width="500">
<Border Name="border" Padding="10">
<Path x:Name="icon" Data="{Binding IconData}" Stretch="Fill" Fill="{DynamicResource Accent}" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Border>
<TextBlock x:Name="texto" ToolTip="{Binding Tooltip}" Text="{Binding DisplayName}" Margin="45,2,2,2" FontSize="{DynamicResource MediumFontSize}" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" HorizontalAlignment="Left" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IconData}" Value="{x:Null}">
<Setter Property="Margin" TargetName="texto">
<Setter.Value>
<Thickness Bottom="2" Top="2" Left="10" Right="2"/>
</Setter.Value>
</Setter>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Trigger.Setters>
<Setter Property="Fill" TargetName="icon">
<Setter.Value>
<SolidColorBrush Color="#f2f2f2" />
</Setter.Value>
</Setter>
</Trigger.Setters>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ListBoxItem.IsMouseOver" SourceName="LinkList" Value="true">
<Trigger.Setters>
<Setter Property="SelectedLinkGroup" Value="{Binding Source=LinkList,Path=Children}"/>
</Trigger.Setters>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
In the code below I am trying to set the value SelectedLinkGroup From property to the value of Children referring to the ListBoxItem LinkList.
<Setter Property="SelectedLinkGroup" Value="{Binding Source=LinkList,Path=Children}"/>
using FirstFloor.ModernUI.Presentation;
using System;
using System.Data;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace FirstFloor.ModernUI.Windows.Controls
{
/// <summary>
/// Represents a control that contains multiple pages that share the same space on screen.
/// </summary>
public class ModernVerticalMenu
: Control
{
/// <summary>
/// Identifies the ContentLoader dependency property.
/// </summary>
public static readonly DependencyProperty ContentLoaderProperty = DependencyProperty.Register("ContentLoader", typeof(IContentLoader), typeof(ModernVerticalMenu), new PropertyMetadata(new DefaultContentLoader()));
/// <summary>
/// Identifies the ListWidth dependency property.
/// </summary>
public static readonly DependencyProperty ListWidthProperty = DependencyProperty.Register("ListWidth", typeof(GridLength), typeof(ModernVerticalMenu), new PropertyMetadata(new GridLength(170)));
/// <summary>
/// Identifies the Links dependency property.
/// </summary>
public static readonly DependencyProperty LinksProperty = DependencyProperty.Register("Links", typeof(LinkCollection), typeof(ModernVerticalMenu), new PropertyMetadata(OnLinksChanged));
/// <summary>
/// Identifies the SelectedSource dependency property.
/// </summary>
public static readonly DependencyProperty SelectedSourceProperty = DependencyProperty.Register("SelectedSource", typeof(Uri), typeof(ModernVerticalMenu), new PropertyMetadata(OnSelectedSourceChanged));
/// <summary>
/// Defines the SelectedLinkGroup dependency property.
/// </summary>
public static readonly DependencyProperty SelectedLinkGroupProperty = DependencyProperty.Register("SelectedLinkGroup", typeof(LinkCollection), typeof(ModernVerticalMenu), new PropertyMetadata(OnSelectedLinkGroupChanged));
/// <summary>
/// Defines the SelectedLink dependency property.
/// </summary>
public static readonly DependencyProperty SelectedLinkProperty = DependencyProperty.Register("SelectedLink", typeof(Link), typeof(ModernVerticalMenu), new PropertyMetadata(OnSelectedLinkChanged));
/// <summary>
/// Defines the SelectedLink dependency property.
/// </summary>
public static readonly DependencyProperty BackColorProperty = DependencyProperty.Register("BackColor", typeof(SolidColorBrush), typeof(ModernVerticalMenu), new PropertyMetadata(null));
/// <summary>
/// Occurs when the selected source has changed.
/// </summary>
public event EventHandler<SourceEventArgs> SelectedSourceChanged;
private ListBox linkList;
/// <summary>
/// Initializes a new instance of the <see cref="ModernVerticalMenu"/> control.
/// </summary>
public ModernVerticalMenu()
{
this.DefaultStyleKey = typeof(ModernVerticalMenu);
// this.BackColor = new SolidColorBrush(Color.FromRgb(0,0,255));
// create a default links collection
SetCurrentValue(LinksProperty, new LinkCollection());
}
private static void OnSelectedLinkGroupChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
// retrieve the selected link from the group
var group = (LinkCollection)e.NewValue; // cria uma nova instancia do grupo
Link selectedLink = null; //cria um link selecionado
if (group != null)
{ //se o grupo copiado existe
selectedLink = group.FirstOrDefault();
}
// update the selected link
((ModernVerticalMenu)o).SetCurrentValue(SelectedLinkProperty, selectedLink);
}
private static void OnSelectedLinkChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
// update selected source
var newValue = (Link)e.NewValue;
Uri selectedSource = null;
if (newValue != null)
{
selectedSource = newValue.Source;
}
((ModernVerticalMenu)o).SetCurrentValue(SelectedSourceProperty, selectedSource);
}
private static void OnLinksChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((ModernVerticalMenu)o).UpdateSelection();
}
private static void OnSelectedSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((ModernVerticalMenu)o).OnSelectedSourceChanged((Uri)e.OldValue, (Uri)e.NewValue);
}
private void OnSelectedSourceChanged(Uri oldValue, Uri newValue)
{
UpdateSelection();
// raise SelectedSourceChanged event
var handler = this.SelectedSourceChanged;
if (handler != null) {
handler(this, new SourceEventArgs(newValue));
}
}
private void UpdateSelection()
{
if (this.linkList == null || this.Links == null) {
return;
}
// sync list selection with current source
this.linkList.SelectedItem = this.Links.FirstOrDefault(l => l.Source == this.SelectedSource);
// SetValue(SelectedLinkGroupProperty, this.Links.FirstOrDefault(l => l.Children == this.SelectedLinkGroup));
if (this.Links.FirstOrDefault(l => l.Children == this.SelectedLinkGroup) != null) { }
}
/// <summary>
/// When overridden in a derived class, is invoked whenever application code or internal processes call System.Windows.FrameworkElement.ApplyTemplate().
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (this.linkList != null) {
this.linkList.SelectionChanged -= OnLinkListSelectionChanged;
}
this.linkList = GetTemplateChild("LinkList") as ListBox;
if (this.linkList != null) {
this.linkList.SelectionChanged += OnLinkListSelectionChanged;
}
UpdateSelection();
}
private void OnLinkListSelectionChanged(object sender, SelectionChangedEventArgs e)
{
//
var link = this.linkList.SelectedItem as Link;
if (link != null && link.Source != this.SelectedSource)
{
SetCurrentValue(SelectedSourceProperty, link.Source);
SetCurrentValue(SelectedLinkGroupProperty, link.Children);
}
}
/// <summary>
/// Gets or sets the content loader.
/// </summary>
public IContentLoader ContentLoader
{
get { return (IContentLoader)GetValue(ContentLoaderProperty); }
set { SetValue(ContentLoaderProperty, value); }
}
/// <summary>
/// Gets or sets the collection of links that define the available content in this tab.
/// </summary>
public LinkCollection Links
{
get { return (LinkCollection)GetValue(LinksProperty); }
set { SetValue(LinksProperty, value); }
}
/// <summary>
/// Gets or sets the collection of links that define the available content in this tab.
/// </summary>
public LinkCollection SelectedLinkGroup
{
get { return (LinkCollection)GetValue(SelectedLinkGroupProperty); }
set { SetValue(SelectedLinkGroupProperty, value); }
}
/// <summary>
/// Gets or sets the collection of links that define the available content in this tab.
/// </summary>
public Link SelectedLink
{
get { return (Link)GetValue(SelectedLinkProperty); }
set { SetValue(SelectedLinkProperty, value); }
}
/// <summary>
/// Gets or sets the width of the list when Layout is set to List.
/// </summary>
/// <value>
/// The width of the list.
/// </value>
public GridLength ListWidth
{
get { return (GridLength)GetValue(ListWidthProperty); }
set { SetValue(ListWidthProperty, value); }
}
/// <summary>
/// Gets or sets the source URI of the selected link.
/// </summary>
/// <value>The source URI of the selected link.</value>
public Uri SelectedSource
{
get { return (Uri)GetValue(SelectedSourceProperty); }
set { SetValue(SelectedSourceProperty, value); }
}
/// <summary>
/// Gets or sets the source URI of the selected link.
/// </summary>
/// <value>The source URI of the selected link.</value>
public SolidColorBrush BackColor
{
get { return (SolidColorBrush)GetValue(BackColorProperty); }
set { SetValue(BackColorProperty, value); }
}
}
}
From my understanding of the code, you are trying to set the SelectedLinkGroup property to the items in your LinkList when your mouse is over a ListBoxItem.
As your LinkList's ItemsSource is bound to the Links property on your control you should be able to bind directly to this property to obtain the same result. The following code does just that.
Value="{Binding Links, RelativeSource={RelativeSource TemplatedParent}}"
Alternatively you could bind to the LinkList Items directly.
Value="{Binding ElementName=LinkList, Path=ItemsSource}"
Here you specify the name of the element to bind to and the property on that element.

MenuItem Passing selected item to ViewModel via RelayCommand ala MVVM-Light & Header

I've been working off of this Q/A and a bunch of others to try and figure this out, but I must be missing something simple:
Bind Items to MenuItem -> use Command
I created this little test application to try and understand context menus and learn about how to wire up the click events to relay commands in the ViewModel, and get access to the currently selected item from the context menu.
Here is the XAML:
<Window x:Class="ContextMenuTest_01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="105" Width="525"
WindowStartupLocation="CenterScreen"
xmlns:local="clr-namespace:ContextMenuTest_01.ViewModels"
DataContext="MainWindowViewModel">
<Window.Resources>
<ObjectDataProvider x:Key="MainWindowViewModel" ObjectType="{x:Type local:MainWindowViewModel}" IsAsynchronous="True"/>
</Window.Resources>
<!-- CONTEXT MENU -->
<Window.ContextMenu>
<ContextMenu DataContext="MainWindowViewModel" Name="MainWindowContextMenu" PresentationTraceSources.TraceLevel="High">
<MenuItem Header="Skins" ItemsSource="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins.SkinName}"/>
<Setter Property="Command" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=ContextMenuClickCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=SkinName}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
</Window.ContextMenu>
</Window>
ViewModel:
using ContextMenuTest_01.Models;
using System.Collections.ObjectModel;
using ContextMenuTest_01.CommandBase;
using System.Windows;
namespace ContextMenuTest_01.ViewModels
{
/// <summary>Main Window View Model</summary>
class MainWindowViewModel
{
#region Class Variables
/// <summary>The skins</summary>
private ObservableCollection<SkinItem> skins = new ObservableCollection<SkinItem>();
#endregion Class Variables
public RelayCommand<object> ContextMenuClickCommand{ get; private set; }
#region Properties
/// <summary>Gets the skins.</summary>
/// <value>The skins.</value>
public ObservableCollection<SkinItem> Skins
{
get { return this.skins; }
private set { this.skins = value; }
}
#endregion Properties
/// <summary>Initializes a new instance of the <see cref="MainWindowViewModel"/> class.</summary>
public MainWindowViewModel()
{
ContextMenuClickCommand = new RelayCommand<object>((e) => OnMenuItemClick(e));
skins.Add(new SkinItem("Skin Item 1"));
skins.Add(new SkinItem("Skin Item 2"));
skins.Add(new SkinItem("Skin Item 3"));
}
/// <summary>Called when [menu item click].</summary>
public void OnMenuItemClick(object selected)
{
MessageBox.Show("Got to the ViewModel! YAY!!!");
}
}
}
So the function OnMenuItemClick will get hit in the debugger and the message box is shown if I change the following three lines in the ViewModel:
Remove the from the Relay Command definition:
public RelayCommand ContextMenuClickCommand{ get; private set; }
Remove the and (e) from where the RelayCommand is created:
ContextMenuClickCommand = new RelayCommand(() => OnMenuItemClick());
Remove the (object selected) from the OnMenuItemClick public function:
public void OnMenuItemClick()
Then everything works but of course I don't have the currently selected item.
So what am I missing in the XAML that would command parameter from passing the argument SkinName to the RelayCommand?
Also if I leave off the line:
<Setter Property="Header" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins.SkinName}"/>
Then I get the following in my context menu:
Skins -> ContextMenuTest_01.Models.SkinItem
ContextMenuTest_01.Models.SkinItem
ContextMenuTest_01.Models.SkinItem
Which tells me the binding is working correctly, it's just not displaying it correctly, which is why I tried to insert the
<Setter Property="Header"....
but of course that's not working as I would expect it to.
Thanks for your time! Any ideas would be helpful!
I don't have anything in the code behind which is the way it should be when following MVVM.
Here is my skinItem class, not much to speak of, but I figured I would show it before someone asks about it:
using System.Windows.Input;
using System.Windows.Media;
namespace ContextMenuTest_01.Models
{
/// <summary>A small data structure to hold a single skin item.</summary>
public class SkinItem
{
#region Class Variables
/// <summary>The skin name</summary>
private string skinName;
/// <summary>The base skin name</summary>
private string baseSkinName;
/// <summary>The skin path</summary>
private string skinPath;
/// <summary>The action to be taken when switching skins.</summary>
private ICommand action;
/// <summary>The icon of the skin.</summary>
private Brush icon;
#endregion Class Variables
#region Constructors
/// <summary>Initializes a new instance of the <see cref="SkinItem"/> class.</summary>
public SkinItem() { }
/// <summary>Initializes a new instance of the <see cref="SkinItem" /> class.</summary>
/// <param name="newSkinName">The name of the new skin.</param>
/// <param name="baseSkinName">Name of the base skin.</param>
/// <param name="newSkinPath">Optional Parameter: The new skin path.</param>
/// <param name="newSkinAction">Optional Parameter: The new skin action to be taken when switching to the new skin.</param>
/// <param name="newSkinIcon">Optional Parameter: The new skin icon.</param>
public SkinItem(string newSkinName, string baseSkinName = "", string newSkinPath = "", ICommand newSkinAction = null, Brush newSkinIcon = null)
{
if (newSkinName != "")
this.skinName = newSkinName;
if (baseSkinName != "")
this.baseSkinName = baseSkinName;
if (newSkinPath != "")
this.skinPath = newSkinPath;
if (newSkinAction != null)
this.action = newSkinAction;
if (newSkinIcon != null)
this.icon = newSkinIcon;
}
#endregion Constructors
#region Properties
/// <summary>Gets or sets the name of the skin.</summary>
/// <value>The name of the skin.</value>
public string SkinName
{
get { return this.skinName; }
set
{
if (this.skinName != value)
{
this.skinName = value;
//OnPropertyChanged(() => this.SkinName);
}
}
}
/// <summary>Gets or sets the name of the base skin.</summary>
/// <value>The name of the base skin.</value>
public string BaseSkinName
{
get { return this.baseSkinName; }
set
{
if (this.baseSkinName != value)
{
this.baseSkinName = value;
//OnPropertyChanged(() => this.BaseSkinName);
}
}
}
/// <summary>Gets or sets the skin path.</summary>
/// <value>The skin path.</value>
public string SkinPath
{
get { return this.skinPath; }
set
{
if (this.skinPath != value)
{
this.skinPath = value;
//OnPropertyChanged(() => this.SkinPath);
}
}
}
/// <summary>Gets or sets the action.</summary>
/// <value>The action.</value>
public ICommand Action
{
get { return this.action; }
set
{
if (this.action != value)
{
this.action = value;
//OnPropertyChanged(() => this.Action);
}
}
}
/// <summary>Gets or sets the icon.</summary>
/// <value>The icon.</value>
public Brush Icon
{
get { return this.icon; }
set
{
if (this.icon != value)
{
this.icon = value;
//OnPropertyChanged(() => this.Icon);
}
}
}
#endregion Properties
}
}
Oh and I am using Galasoft MVVM-Light generic RelayCommand which is supposed to take parameters, can be found here:
http://mvvmlight.codeplex.com/SourceControl/latest#GalaSoft.MvvmLight/GalaSoft.MvvmLight%20%28NET35%29/Command/RelayCommandGeneric.cs
I'm having a little trouble understanding exactly what you're looking for. But in running your code I see that the names of the Skins are not showing in the context menu. If you remove the source so your setting looks like this for the header:
<!-- CONTEXT MENU -->
<Window.ContextMenu>
<ContextMenu DataContext="MainWindowViewModel" Name="MainWindowContextMenu" PresentationTraceSources.TraceLevel="High">
<MenuItem Header="Skins" ItemsSource="{Binding Source={StaticResource MainWindowViewModel}, Path=Skins}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=SkinName}"/>
<Setter Property="Command" Value="{Binding Source={StaticResource MainWindowViewModel}, Path=ContextMenuClickCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=SkinName}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
</Window.ContextMenu>
That will fix your problem. Since you are setting the source on the MenuItem, you change the datacontext for the items within. So you don't need to specify the source again.
Edit:
Also I changed the Path from Skins.SkinName to SkinName
Now I see the text for the items in the menu and when I click on say "Skin Item 1", the value of selected in OnMenuItemClick is "Skin Item 1".

Attached Property with WaterMark Textbox

I want a TextBox with a WaterMark text. I use this solution, which works fine as provided.
Since I have a couple TextBoxes in the control I want to make it a bit dynamic. So I use ( the first time) an attached property, but I can't make it work. No compile errors, but in XAML the Tag Statement can not be resolved ... Content={Binding Path=view:SomeClass.Tag, RelativeSource=...
What is wrong here?
I did this in XAML
<StackPanel Grid.Row="1" TextBlock.FontSize="12">
<TextBox Style="{DynamicResource TextBoxWaterMark}" view:SomeClass.Tag="Search" />
</StackPanel>
Style
<RibbonWindow.Resources>
<Style xmlns:sys="clr-namespace:System;assembly=mscorlib"
x:Key="TextBoxWaterMark"
TargetType="{x:Type TextBox}">
<Style.Resources>
<VisualBrush x:Key="CueBannerBrush"
AlignmentX="Left"
AlignmentY="Center"
Stretch="None">
<VisualBrush.Visual>
<Label Content="{Binding Path=view:SomeClass.Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type view:SomeClass}}}" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="IsMouseCaptured" Value="True">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</RibbonWindow.Resources>
DependencyObject
public static class SomeClass
{
public static readonly DependencyProperty TagProperty = DependencyProperty.RegisterAttached(
"Tag",
typeof(object),
typeof(SomeClass),
new FrameworkPropertyMetadata(null));
public static object GetTag(DependencyObject dependencyObject)
{
return dependencyObject.GetValue(TagProperty);
}
public static void SetTag(DependencyObject dependencyObject, object value)
{
dependencyObject.SetValue(TagProperty, value);
}
}
You Can create such type attached property. here see how.
Mostly peoples looking for watermask textboxs what about combobo items controls etc. lets cover these all at once.
create AttachedProperty like .
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
/// <summary>
/// Class that provides the Watermark attached property
/// </summary>
public static class WatermarkService
{
/// <summary>
/// Watermark Attached Dependency Property
/// </summary>
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
"Watermark",
typeof(object),
typeof(WatermarkService),
new FrameworkPropertyMetadata((object)null, new PropertyChangedCallback(OnWatermarkChanged)));
#region Private Fields
/// <summary>
/// Dictionary of ItemsControls
/// </summary>
private static readonly Dictionary<object, ItemsControl> itemsControls = new Dictionary<object, ItemsControl>();
#endregion
/// <summary>
/// Gets the Watermark property. This dependency property indicates the watermark for the control.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> to get the property from</param>
/// <returns>The value of the Watermark property</returns>
public static object GetWatermark(DependencyObject d)
{
return (object)d.GetValue(WatermarkProperty);
}
/// <summary>
/// Sets the Watermark property. This dependency property indicates the watermark for the control.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> to set the property on</param>
/// <param name="value">value of the property</param>
public static void SetWatermark(DependencyObject d, object value)
{
d.SetValue(WatermarkProperty, value);
}
/// <summary>
/// Handles changes to the Watermark property.
/// </summary>
/// <param name="d"><see cref="DependencyObject"/> that fired the event</param>
/// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Control control = (Control)d;
control.Loaded += Control_Loaded;
if (d is ComboBox || d is TextBox)
{
control.GotKeyboardFocus += Control_GotKeyboardFocus;
control.LostKeyboardFocus += Control_Loaded;
}
if (d is ItemsControl && !(d is ComboBox))
{
ItemsControl i = (ItemsControl)d;
// for Items property
i.ItemContainerGenerator.ItemsChanged += ItemsChanged;
itemsControls.Add(i.ItemContainerGenerator, i);
// for ItemsSource property
DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, i.GetType());
prop.AddValueChanged(i, ItemsSourceChanged);
}
}
#region Event Handlers
/// <summary>
/// Handle the GotFocus event on the control
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
private static void Control_GotKeyboardFocus(object sender, RoutedEventArgs e)
{
Control c = (Control)sender;
if (ShouldShowWatermark(c))
{
RemoveWatermark(c);
}
}
/// <summary>
/// Handle the Loaded and LostFocus event on the control
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="RoutedEventArgs"/> that contains the event data.</param>
private static void Control_Loaded(object sender, RoutedEventArgs e)
{
Control control = (Control)sender;
if (ShouldShowWatermark(control))
{
ShowWatermark(control);
}
}
/// <summary>
/// Event handler for the items source changed event
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
private static void ItemsSourceChanged(object sender, EventArgs e)
{
ItemsControl c = (ItemsControl)sender;
if (c.ItemsSource != null)
{
if (ShouldShowWatermark(c))
{
ShowWatermark(c);
}
else
{
RemoveWatermark(c);
}
}
else
{
ShowWatermark(c);
}
}
/// <summary>
/// Event handler for the items changed event
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">A <see cref="ItemsChangedEventArgs"/> that contains the event data.</param>
private static void ItemsChanged(object sender, ItemsChangedEventArgs e)
{
ItemsControl control;
if (itemsControls.TryGetValue(sender, out control))
{
if (ShouldShowWatermark(control))
{
ShowWatermark(control);
}
else
{
RemoveWatermark(control);
}
}
}
#endregion
#region Helper Methods
/// <summary>
/// Remove the watermark from the specified element
/// </summary>
/// <param name="control">Element to remove the watermark from</param>
private static void RemoveWatermark(UIElement control)
{
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);
// layer could be null if control is no longer in the visual tree
if (layer != null)
{
Adorner[] adorners = layer.GetAdorners(control);
if (adorners == null)
{
return;
}
foreach (Adorner adorner in adorners)
{
if (adorner is WatermarkAdorner)
{
adorner.Visibility = Visibility.Hidden;
layer.Remove(adorner);
}
}
}
}
/// <summary>
/// Show the watermark on the specified control
/// </summary>
/// <param name="control">Control to show the watermark on</param>
private static void ShowWatermark(Control control)
{
AdornerLayer layer = AdornerLayer.GetAdornerLayer(control);
// layer could be null if control is no longer in the visual tree
if (layer != null)
{
layer.Add(new WatermarkAdorner(control, GetWatermark(control)));
}
}
/// <summary>
/// Indicates whether or not the watermark should be shown on the specified control
/// </summary>
/// <param name="c"><see cref="Control"/> to test</param>
/// <returns>true if the watermark should be shown; false otherwise</returns>
private static bool ShouldShowWatermark(Control c)
{
if (c is ComboBox)
{
return (c as ComboBox).Text == string.Empty;
}
else if (c is TextBoxBase)
{
return (c as TextBox).Text == string.Empty;
}
else if (c is ItemsControl)
{
return (c as ItemsControl).Items.Count == 0;
}
else
{
return false;
}
}
#endregion
}
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
/// <summary>
/// Adorner for the watermark
/// </summary>
internal class WatermarkAdorner : Adorner
{
#region Private Fields
/// <summary>
/// <see cref="ContentPresenter"/> that holds the watermark
/// </summary>
private readonly ContentPresenter contentPresenter;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="WatermarkAdorner"/> class
/// </summary>
/// <param name="adornedElement"><see cref="UIElement"/> to be adorned</param>
/// <param name="watermark">The watermark</param>
public WatermarkAdorner(UIElement adornedElement, object watermark) :
base(adornedElement)
{
this.IsHitTestVisible = false;
this.contentPresenter = new ContentPresenter();
this.contentPresenter.Content = watermark;
this.contentPresenter.Opacity = 0.5;
this.contentPresenter.Margin = new Thickness(Control.Margin.Left + Control.Padding.Left, Control.Margin.Top + Control.Padding.Top, 0, 0);
if (this.Control is ItemsControl && !(this.Control is ComboBox))
{
this.contentPresenter.VerticalAlignment = VerticalAlignment.Center;
this.contentPresenter.HorizontalAlignment = HorizontalAlignment.Center;
}
// Hide the control adorner when the adorned element is hidden
Binding binding = new Binding("IsVisible");
binding.Source = adornedElement;
binding.Converter = new BooleanToVisibilityConverter();
this.SetBinding(VisibilityProperty, binding);
}
#endregion
#region Protected Properties
/// <summary>
/// Gets the number of children for the <see cref="ContainerVisual"/>.
/// </summary>
protected override int VisualChildrenCount
{
get { return 1; }
}
#endregion
#region Private Properties
/// <summary>
/// Gets the control that is being adorned
/// </summary>
private Control Control
{
get { return (Control)this.AdornedElement; }
}
#endregion
#region Protected Overrides
/// <summary>
/// Returns a specified child <see cref="Visual"/> for the parent <see cref="ContainerVisual"/>.
/// </summary>
/// <param name="index">A 32-bit signed integer that represents the index value of the child <see cref="Visual"/>. The value of index must be between 0 and <see cref="VisualChildrenCount"/> - 1.</param>
/// <returns>The child <see cref="Visual"/>.</returns>
protected override Visual GetVisualChild(int index)
{
return this.contentPresenter;
}
/// <summary>
/// Implements any custom measuring behavior for the adorner.
/// </summary>
/// <param name="constraint">A size to constrain the adorner to.</param>
/// <returns>A <see cref="Size"/> object representing the amount of layout space needed by the adorner.</returns>
protected override Size MeasureOverride(Size constraint)
{
// Here's the secret to getting the adorner to cover the whole control
this.contentPresenter.Measure(Control.RenderSize);
return Control.RenderSize;
}
/// <summary>
/// When overridden in a derived class, positions child elements and determines a size for a <see cref="FrameworkElement"/> derived class.
/// </summary>
/// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
/// <returns>The actual size used.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
this.contentPresenter.Arrange(new Rect(finalSize));
return finalSize;
}
#endregion
}
Sample to Use this attached Property.
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Height="403"
Width="346"
Title="Window1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<AdornerDecorator Grid.Row="0">
<TextBox VerticalAlignment="Center" >
<local:WatermarkService.Watermark>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12">TextBox Water Mask </TextBlock>
</local:WatermarkService.Watermark>
</TextBox>
</AdornerDecorator>
<AdornerDecorator Grid.Row="1">
<ComboBox ItemsSource="{Binding Items}">
<local:WatermarkService.Watermark>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12">Combo Box WaterMask</TextBlock>
</local:WatermarkService.Watermark>
</ComboBox>
</AdornerDecorator>
</Grid>
</Window>
For attached properties you need to use parentheses in your binding path:
<Label Content="{Binding Path=(view:SomeClass.Tag)}" />
This is written here along with explanations on how to bind to other types, such as indexers and collection views.

Categories