1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00

Implemented conditions logic

This commit is contained in:
SpoinkyNL 2016-03-22 17:09:00 +01:00
parent fa30bd657b
commit 2cb8cac90d
17 changed files with 381 additions and 184 deletions

View File

@ -253,9 +253,6 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="ArtemisBootstrapper.cs" />
<Compile Include="Components\Abstract\LayerComponent.cs" />
<Compile Include="Components\Layer.cs" />
<Compile Include="Components\LayerComposite.cs" />
<Compile Include="DAL\ProfileProvider.cs" />
<Compile Include="Events\ToggleEnabled.cs" />
<Compile Include="Events\ActiveEffectChanged.cs" />
@ -278,8 +275,11 @@
<Compile Include="Models\EffectSettings.cs" />
<Compile Include="Models\GameSettings.cs" />
<Compile Include="Models\Interfaces\IGameDataModel.cs" />
<Compile Include="Models\LayerConditionModel.cs" />
<Compile Include="Models\ProfileModel.cs" />
<Compile Include="Models\Profiles\LayerConditionModel.cs" />
<Compile Include="Models\Profiles\LayerModel.cs" />
<Compile Include="Models\Profiles\LayerDynamicPropertiesModel.cs" />
<Compile Include="Models\Profiles\LayerPropertiesModel.cs" />
<Compile Include="Models\Profiles\ProfileModel.cs" />
<Compile Include="Modules\Effects\AmbientLightning\AmbientLightningEffectModel.cs" />
<Compile Include="Modules\Effects\AmbientLightning\AmbientLightningEffectSettings.cs" />
<Compile Include="Modules\Effects\AmbientLightning\AmbientLightningEffectView.xaml.cs">
@ -324,6 +324,7 @@
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="Modules\Games\RocketLeague\RocketLeagueDataModel.cs" />
<Compile Include="Modules\Games\RocketLeague\RocketLeagueModel.cs" />
<Compile Include="Modules\Games\TheDivision\TheDivision.Designer.cs">
<AutoGen>True</AutoGen>
@ -519,6 +520,7 @@
<Resource Include="Resources\logo-disabled.ico" />
<Resource Include="Resources\Dota2\dotaGamestateConfiguration.txt" />
<None Include="Resources\LogitechLED.dll" />
<None Include="Resources\folder.png" />
<Content Include="Resources\Witcher3\playerWitcher.txt" />
<Content Include="Resources\Witcher3\artemis.txt" />
<Resource Include="Resources\Entypo.ttf" />

View File

@ -1,21 +0,0 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Artemis.Models;
using Artemis.Models.Interfaces;
namespace Artemis.Components.Abstract
{
public abstract class LayerComponent
{
public string Name { get; set; }
public List<LayerConditionModel> ConditionModels { get; set; }
public bool ConditionsMet(IGameDataModel dataModel)
{
return ConditionModels.All(cm => cm.ConditionMet(dataModel));
}
public abstract void Draw(Graphics g);
}
}

View File

@ -1,13 +0,0 @@
using System.Drawing;
using Artemis.Components.Abstract;
namespace Artemis.Components
{
public class Layer : LayerComponent
{
public override void Draw(Graphics g)
{
// Read properties and draw accordingly
}
}
}

View File

@ -1,17 +0,0 @@
using System.Collections.Generic;
using System.Drawing;
using Artemis.Components.Abstract;
namespace Artemis.Components
{
public class LayerComposite : LayerComponent
{
public List<LayerComponent> LayerComponents { get; set; }
public override void Draw(Graphics g)
{
foreach (var layerComponent in LayerComponents)
layerComponent.Draw(g);
}
}
}

View File

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using Artemis.Models;
using Artemis.Models.Profiles;
using Newtonsoft.Json;
namespace Artemis.DAL

View File

@ -2,7 +2,6 @@
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Artemis.Events;
using Artemis.Models;
using Artemis.Services;

View File

@ -1,7 +1,8 @@
using System.Collections.Generic;
using System.Linq.Dynamic;
using Artemis.Models.Interfaces;
namespace Artemis.Models
namespace Artemis.Models.Profiles
{
public class LayerConditionModel
{
@ -9,11 +10,11 @@ namespace Artemis.Models
public string Value { get; set; }
public string Operator { get; set; }
public bool ConditionMet(object subject)
public bool ConditionMet<T>(IGameDataModel subject)
{
// Put the subject in a list, allowing Dynamic Linq to be used.
var subjectList = new List<object> {subject};
var res = subjectList.Where($"s => s.{Field} {Operator} {Value}").Any();
var subjectList = new List<T> {(T) subject};
var res = subjectList.Where($"{Field} {Operator} {Value}").Any();
return res;
}
}

View File

@ -0,0 +1,86 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq.Dynamic;
using System.Reflection;
using Artemis.Models.Interfaces;
namespace Artemis.Models.Profiles
{
public class LayerDynamicPropertiesModel
{
public string LayerProperty { get; set; }
public string GameProperty { get; set; }
public string RequiredOperator { get; set; }
public string RequiredValue { get; set; }
public LayerPopertyType LayerPopertyType { get; set; }
/// <summary>
/// Only used when LayerPropertyType is PercentageOf or PercentageOfProperty
/// </summary>
public string PercentageSource { get; set; }
/// <summary>
/// Only used when LayerPropertyType is Color
/// </summary>
public List<Color> LayerColors { get; set; }
internal void ApplyProperty<T>(IGameDataModel dataModel, LayerPropertiesModel userProps,
LayerPropertiesModel props)
{
var dataList = new List<T> {(T) dataModel};
// Attempt to set the property
var layerProp = props.GetType().GetProperty(LayerProperty);
var layerUserProp = userProps.GetType().GetProperty(LayerProperty);
if (LayerPopertyType == LayerPopertyType.PercentageOf)
SetPercentageOf(props, userProps, dataModel, int.Parse(PercentageSource));
if (LayerPopertyType == LayerPopertyType.PercentageOfProperty)
SetPercentageOfProperty(props, userProps, dataModel);
if (LayerPopertyType == LayerPopertyType.Color)
{
if (dataList.Where($"{GameProperty} {RequiredOperator} {RequiredValue}").Any())
SetColor(layerProp, dataModel);
}
}
private void SetPercentageOf(LayerPropertiesModel props, LayerPropertiesModel userProps,
IGameDataModel dataModel, int percentageSource)
{
// Property that will be set
var layerProp = props.GetType().GetProperty(LayerProperty);
// Property to use as a 100%
var userProp = userProps.GetType().GetProperty(LayerProperty);
// Value to use as a source
var source = dataModel.GetType().GetProperty(GameProperty)?.GetValue(dataModel, null);
if (layerProp == null || userProp == null || source == null)
return;
var percentage = double.Parse(source.ToString())/percentageSource;
layerProp.SetValue(props, (int) (percentage*(int) userProp.GetValue(userProps, null)));
}
private void SetPercentageOfProperty(LayerPropertiesModel props, LayerPropertiesModel userProps,
IGameDataModel dataModel)
{
var value = dataModel.GetType().GetProperty(PercentageSource)?.GetValue(dataModel, null);
if (value != null)
SetPercentageOf(props, userProps, dataModel, (int) value);
}
private void SetColor(PropertyInfo layerProp, IGameDataModel dataModel)
{
if (layerProp == null)
return;
if (layerProp.PropertyType == typeof (List<Color>))
layerProp.SetValue(dataModel, LayerColors, null);
}
}
public enum LayerPopertyType
{
PercentageOf,
PercentageOfProperty,
Color
}
}

View File

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Models.Interfaces;
using Artemis.Properties;
using Newtonsoft.Json;
using Color = System.Drawing.Color;
using Pen = System.Drawing.Pen;
namespace Artemis.Models.Profiles
{
public class LayerModel
{
public LayerModel(string name, LayerType layerType)
{
Name = name;
LayerType = layerType;
LayerUserProperties = new LayerPropertiesModel();
LayerCalculatedProperties = new LayerPropertiesModel();
Children = new List<LayerModel>();
LayerConditions = new List<LayerConditionModel>();
LayerProperties = new List<LayerDynamicPropertiesModel>();
}
public string Name { get; set; }
public LayerType LayerType { get; set; }
public LayerPropertiesModel LayerUserProperties { get; set; }
[JsonIgnore]
public LayerPropertiesModel LayerCalculatedProperties { get; }
public List<LayerModel> Children { get; set; }
public List<LayerConditionModel> LayerConditions { get; set; }
public List<LayerDynamicPropertiesModel> LayerProperties { get; set; }
public ImageSource LayerImage => GetPreviewImage();
private BitmapImage GetPreviewImage()
{
var bitmap = new Bitmap(18, 18);
using (var g = Graphics.FromImage(bitmap))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
if (LayerType == LayerType.Ellipse)
{
g.FillEllipse(new SolidBrush(LayerUserProperties.Colors.FirstOrDefault()), 0, 0, 18, 18);
g.DrawEllipse(new Pen(Color.Black, 1), 0, 0, 17, 17);
}
else if (LayerType == LayerType.Rectangle)
{
g.FillRectangle(new SolidBrush(LayerUserProperties.Colors.FirstOrDefault()), 0, 0, 18, 18);
g.DrawRectangle(new Pen(Color.Black, 1), 0, 0, 17, 17);
}
else
{
bitmap = Resources.folder;
}
}
using (var memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
return bitmapImage;
}
}
public bool ConditionsMet<T>(IGameDataModel dataModel)
{
return LayerConditions.All(cm => cm.ConditionMet<T>(dataModel));
}
public void Draw<T>(IGameDataModel dataModel, Graphics g)
{
if (!ConditionsMet<T>(dataModel))
return;
Update<T>(dataModel);
switch (LayerType)
{
case LayerType.Folder:
DrawChildren<T>(dataModel, g);
break;
case LayerType.Rectangle:
DrawRectangle(g);
break;
case LayerType.Ellipse:
DrawEllipse(g);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void Update<T>(IGameDataModel dataModel)
{
foreach (var dynamicProperty in LayerProperties)
dynamicProperty.ApplyProperty<T>(dataModel, LayerUserProperties, LayerCalculatedProperties);
}
private void DrawChildren<T>(IGameDataModel dataModel, Graphics g)
{
foreach (var layerModel in Children)
layerModel.Draw<T>(dataModel, g);
}
private void DrawRectangle(Graphics g)
{
}
private void DrawEllipse(Graphics g)
{
}
}
public enum LayerType
{
Folder,
Rectangle,
Ellipse
}
}

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Artemis.Models.Profiles
{
public class LayerPropertiesModel
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Opacity { get; set; }
public bool ContainedBrush { get; set; }
public LinearGradientMode GradientMode { get; set; }
public bool Rotate { get; set; }
public double RotateSpeed { get; set; }
public List<Color> Colors { get; set; }
public LayerPropertiesModel()
{
Colors = new List<Color>();
}
}
}

View File

@ -1,7 +1,6 @@
using System.Collections.Generic;
using Artemis.Components;
namespace Artemis.Models
namespace Artemis.Models.Profiles
{
public class ProfileModel
{
@ -16,7 +15,7 @@ namespace Artemis.Models
public string KeyboardName { get; set; }
public string GameName { get; set; }
public LayerComposite Layers { get; set; }
public List<LayerModel> Layers { get; set; }
protected bool Equals(ProfileModel other)
{
@ -27,7 +26,7 @@ namespace Artemis.Models
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
if (obj.GetType() != GetType()) return false;
return Equals((ProfileModel) obj);
}
@ -35,9 +34,9 @@ namespace Artemis.Models
{
unchecked
{
var hashCode = (Name != null ? Name.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (KeyboardName != null ? KeyboardName.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (GameName != null ? GameName.GetHashCode() : 0);
var hashCode = Name?.GetHashCode() ?? 0;
hashCode = (hashCode*397) ^ (KeyboardName?.GetHashCode() ?? 0);
hashCode = (hashCode*397) ^ (GameName?.GetHashCode() ?? 0);
return hashCode;
}
}

View File

@ -0,0 +1,9 @@
using Artemis.Models.Interfaces;
namespace Artemis.Modules.Games.RocketLeague
{
internal class RocketLeagueDataModel : IGameDataModel
{
public int Boost { get; set; }
}
}

View File

@ -61,14 +61,14 @@ namespace Artemis.Properties {
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-16&quot;?&gt;
///&lt;!-- Used by Artemis to get the active Sign --&gt;
///&lt;UserConfig&gt;
/// &lt;Group id=&quot;Artemis&quot; displayName=&quot;Artemis&quot;&gt;
/// &lt;VisibleVars&gt;
/// &lt;Var id=&quot;ActiveSign&quot; displayName=&quot;ActiveSign&quot; displayType=&quot;SLIDER:0:1:1000000&quot;/&gt;
/// &lt;/VisibleVars&gt;
/// &lt;/Group&gt;
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-16&quot;?&gt;
///&lt;!-- Used by Artemis to get the active Sign --&gt;
///&lt;UserConfig&gt;
/// &lt;Group id=&quot;Artemis&quot; displayName=&quot;Artemis&quot;&gt;
/// &lt;VisibleVars&gt;
/// &lt;Var id=&quot;ActiveSign&quot; displayName=&quot;ActiveSign&quot; displayType=&quot;SLIDER:0:1:1000000&quot;/&gt;
/// &lt;/VisibleVars&gt;
/// &lt;/Group&gt;
///&lt;/UserConfig&gt;.
/// </summary>
internal static string artemisXml {
@ -88,23 +88,23 @@ namespace Artemis.Properties {
}
/// <summary>
/// Looks up a localized string similar to &quot;Artemis&quot;
///{
/// &quot;uri&quot; &quot;http://localhost:{{port}}/csgo_game_event&quot;
/// &quot;timeout&quot; &quot;5.0&quot;
/// &quot;buffer&quot; &quot;0.1&quot;
/// &quot;throttle&quot; &quot;0.1&quot;
/// &quot;heartbeat&quot; &quot;30.0&quot;
/// &quot;data&quot;
/// {
/// &quot;provider&quot; &quot;1&quot;
/// &quot;map&quot; &quot;1&quot;
/// &quot;round&quot; &quot;1&quot;
/// &quot;player_id&quot; &quot;1&quot;
/// &quot;player_state&quot; &quot;1&quot;
/// &quot;player_weapons&quot; &quot;1&quot;
/// &quot;player_match_stats&quot; &quot;1&quot;
/// }
/// Looks up a localized string similar to &quot;Artemis&quot;
///{
/// &quot;uri&quot; &quot;http://localhost:{{port}}/csgo_game_event&quot;
/// &quot;timeout&quot; &quot;5.0&quot;
/// &quot;buffer&quot; &quot;0.1&quot;
/// &quot;throttle&quot; &quot;0.1&quot;
/// &quot;heartbeat&quot; &quot;30.0&quot;
/// &quot;data&quot;
/// {
/// &quot;provider&quot; &quot;1&quot;
/// &quot;map&quot; &quot;1&quot;
/// &quot;round&quot; &quot;1&quot;
/// &quot;player_id&quot; &quot;1&quot;
/// &quot;player_state&quot; &quot;1&quot;
/// &quot;player_weapons&quot; &quot;1&quot;
/// &quot;player_match_stats&quot; &quot;1&quot;
/// }
///}.
/// </summary>
internal static string csgoGamestateConfiguration {
@ -116,7 +116,7 @@ namespace Artemis.Properties {
/// <summary>
/// Looks up a localized string similar to &quot;Artemis&quot;
///{
/// &quot;uri&quot; &quot;http://localhost:4000/&quot;
/// &quot;uri&quot; &quot;http://localhost:{{port}}/&quot;
/// &quot;timeout&quot; &quot;5.0&quot;
/// &quot;buffer&quot; &quot;0.1&quot;
/// &quot;throttle&quot; &quot;0.1&quot;
@ -138,6 +138,16 @@ namespace Artemis.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap folder {
get {
object obj = ResourceManager.GetObject("folder", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>

View File

@ -142,4 +142,7 @@
<data name="playerWitcherWs" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\witcher3\playerwitcher.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
</data>
<data name="folder" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\folder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,9 +1,16 @@
using System.Linq;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Media;
using Artemis.DAL;
using Artemis.Managers;
using Artemis.Models;
using Artemis.Models.Profiles;
using Artemis.Modules.Games.RocketLeague;
using Artemis.Modules.Games.TheDivision;
using Caliburn.Micro;
using Color = System.Drawing.Color;
namespace Artemis.ViewModels
{
@ -65,8 +72,7 @@ namespace Artemis.ViewModels
}
var profile = new ProfileModel(name, _mainManager.KeyboardManager.ActiveKeyboard.Name, _gameModel.Name);
var test = ProfileProvider.GetAll();
if (test.Contains(profile))
if (ProfileProvider.GetAll().Contains(profile))
{
var overwrite =
await
@ -76,6 +82,44 @@ namespace Artemis.ViewModels
return;
}
// Test
profile.Layers = new List<LayerModel>();
var layerFolder = new LayerModel("[VM TEST] Folder 1", LayerType.Folder);
var layer1 = new LayerModel("[VM TEST] Rectangle 1", LayerType.Rectangle);
layer1.LayerConditions.Add(new LayerConditionModel {Field = "Boost", Operator = ">", Value = "0"});
layer1.LayerProperties.Add(new LayerDynamicPropertiesModel
{
LayerProperty = "Width",
LayerPopertyType = LayerPopertyType.PercentageOf,
GameProperty = "Boost",
PercentageSource = "100"
});
layer1.LayerUserProperties = new LayerPropertiesModel
{
Colors = new List<Color> {Color.Red, Color.OrangeRed},
ContainedBrush = true,
GradientMode = LinearGradientMode.Vertical,
Width = 21,
Height = 7,
Opacity = 100,
Rotate = true,
RotateSpeed = 1,
X = 0,
Y = 0
};
layerFolder.Children.Add(layer1);
layerFolder.Children.Add(new LayerModel("[VM TEST] Ellipse 1", LayerType.Ellipse));
var testData = new RocketLeagueDataModel {Boost = 20};
var bitmap = _mainManager.KeyboardManager.ActiveKeyboard.KeyboardBitmap(4);
using (var g = Graphics.FromImage(bitmap))
{
layerFolder.Draw<RocketLeagueDataModel>(testData, g);
}
// End test
profile.Layers.Add(layerFolder);
ProfileProvider.AddOrUpdate(profile);
LoadProfiles();

View File

@ -20,7 +20,7 @@
<!-- Preview -->
<Label Grid.Column="0" Grid.Row="0" FontSize="20" HorizontalAlignment="Left" Content="Preview" />
<Border Grid.Column="0" Grid.Row="1" Background="#FF232323" BorderBrush="{DynamicResource HighlightBrush}"
BorderThickness="3" Width="800" Height="400">
BorderThickness="3" Width="750" Height="400">
<Border>
<Border.Effect>
<!-- TODO: Pulse 10-20 -->
@ -34,107 +34,41 @@
</Border>
<!-- Profile management -->
<StackPanel Grid.Column="0" Grid.Row="2" Orientation="Horizontal" Margin="0,5,0,0">
<Label Content="Active profile"></Label>
<ComboBox Grid.Row="1" Grid.Column="1" Width="110" VerticalAlignment="Top" x:Name="ProfileModels" DisplayMemberPath="Name" Margin="5,0,0,0" />
<Label Content="Active profile" />
<ComboBox Grid.Row="1" Grid.Column="1" Width="110" VerticalAlignment="Top" x:Name="ProfileModels"
DisplayMemberPath="Name" Margin="5,0,0,0" />
<Button x:Name="AddProfile" Content="Add profile" VerticalAlignment="Top"
Style="{DynamicResource SquareButtonStyle}" Width="95" HorizontalAlignment="Left" Margin="10,0,0,0" />
<Button x:Name="RemoveProfile" Content="Remove profile" VerticalAlignment="Top"
Style="{DynamicResource SquareButtonStyle}" Width="95" HorizontalAlignment="Right" Margin="10,0,0,0" />
Style="{DynamicResource SquareButtonStyle}" Width="95" HorizontalAlignment="Right"
Margin="10,0,0,0" />
</StackPanel>
<!-- Layer list -->
<Label Grid.Column="1" Grid.Row="0" FontSize="20" HorizontalAlignment="Left" Content="Layers" Margin="10,0,0,0" />
<Border Grid.Column="1" Grid.Row="1" Background="#FF232323" BorderBrush="{DynamicResource HighlightBrush}"
BorderThickness="3" Margin="10,0,0,0" Height="400" Width="200">
<TreeView>
BorderThickness="3" Margin="10,0,0,0" Height="400" Width="250">
<TreeView ItemsSource="{Binding Path=SelectedProfileModel.Layers}">
<TreeView.Resources>
<ResourceDictionary
Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />
</TreeView.Resources>
<TreeViewItem>
<!-- Layer example -->
<!-- Content -->
<TreeViewItem.Header>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename" />
<MenuItem Header="Delete" />
<MenuItem Header="Properties" />
</ContextMenu>
</StackPanel.ContextMenu>
<CheckBox VerticalAlignment="Center" ToolTip="Layer enabled" />
<Rectangle Height="18" Width="18" Stroke="Black" VerticalAlignment="Bottom"
Fill="#FFFFE800" />
<TextBlock Margin="5,0,0,0" Text="Layer 1" VerticalAlignment="Center" />
<Image Height="18" Width="18" Source="{Binding LayerImage}" />
<TextBlock Margin="5,0,0,0" Text="{Binding Name}" VerticalAlignment="Center" />
</StackPanel>
</TreeViewItem.Header>
<!-- Context menu -->
<TreeViewItem.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename" />
<MenuItem Header="Delete" />
<MenuItem Header="Properties" />
</ContextMenu>
</TreeViewItem.ContextMenu>
</TreeViewItem>
<TreeViewItem Header="Group 1">
<!-- Context menu -->
<TreeViewItem.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename" />
<MenuItem Header="Delete" />
<MenuItem Header="Properties" />
</ContextMenu>
</TreeViewItem.ContextMenu>
<!-- Group example -->
<TreeViewItem>
<!-- Content -->
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" ToolTip="Layer enabled" />
<Rectangle Height="18" Width="18" Stroke="Black" VerticalAlignment="Bottom"
Fill="#FFC50A0A" />
<TextBlock Margin="5,0,0,0" Text="Layer 2" VerticalAlignment="Center" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
<!-- Circular Layer example -->
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" ToolTip="Layer enabled" />
<Rectangle Height="18" Width="18" Stroke="Black" VerticalAlignment="Bottom"
Fill="#FF0AC50A" RadiusX="18" RadiusY="18" />
<TextBlock Margin="5,0,0,0" Text="Layer 3" VerticalAlignment="Center" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" ToolTip="Layer enabled" />
<Rectangle Height="18" Width="18" Stroke="Black" VerticalAlignment="Bottom" Fill="#FF2C0AC5" />
<TextBlock Margin="5,0,0,0" Text="Layer 4" VerticalAlignment="Center" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="Group 2">
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" ToolTip="Layer enabled" />
<Rectangle Height="18" Width="18" Stroke="Black" VerticalAlignment="Bottom"
Fill="#FF0AC5C5" RadiusX="18" RadiusY="18" />
<TextBlock Margin="5,0,0,0" Text="Layer 5" VerticalAlignment="Center" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" ToolTip="Layer enabled" />
<Rectangle Height="18" Width="18" Stroke="Black" VerticalAlignment="Bottom" Fill="White" RadiusX="18" RadiusY="18" />
<TextBlock Margin="5,0,0,0" Text="Layer 6" VerticalAlignment="Center" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</TreeViewItem>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
<Grid Grid.Column="1" Grid.Row="2" Margin="10,5,0,0">