I currently trying to "port" some control from WindowsForms to WPF.
I Have this stylish led checkbox and try to achieve the same visual appearance in wpf. but I'm unable to get it done.
I've searched a lot butt cannot find a solution to my questions/problems.
This is how the winforms Control looks like
The colored Circle size depends on the size of the control.
The color is user definable. The color is used for the circle and the Text.
It's bright if it ich checked and dimmed / gray when it's unchecked.
The diark and highlight colors are calculated from the control color (lighter/darker).
All my tries to do the same in wpf pretty much failed up to now. :-(
I fist tried to do it with an usercontrol, but decided it would be easier to have it derived from checkbox with just an extra option to set the color.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:LedTest"
xmlns:uc="clr-namespace:WPFTest;assembly=LedControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="LedTest.MainWindow"
Title="MainWindow" Height="285" Width="566">
<Window.Resources>
<ResourceDictionary x:Key="ResDict2" Source="Dictionary2.xaml"/>
</Window.Resources>
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="27" />
<RowDefinition Height="75"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="179*"/>
</Grid.ColumnDefinitions>
<uc:LedControl x:Name="led1"
Color="ForestGreen" Text="Some Option"
Grid.Column="1" Grid.Row="1" Height="39" VerticalAlignment="Bottom" Margin="0,0,0,36"/>
<CheckBox Content="Some Option" Style="{DynamicResource TestStyle}" Margin="0,0,31,0" Grid.Column="1"/>
</Grid>
</Window>
This is my LedControl code:
<UserControl x:Class="LedControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="300">
<UserControl.Resources>
</UserControl.Resources>
<StackPanel x:Name="gridBigLed" Orientation="Horizontal" >
<Border x:Name="border1"
BorderThickness="1"
Width="{Binding ActualHeight, ElementName=gridBigLed, Mode=OneWay}"
CornerRadius="{Binding ActualWidth, ElementName=gridBigLed, Mode=OneWay}"
HorizontalAlignment="Left">
<Border.Background>
<RadialGradientBrush GradientOrigin="0.2,0.2">
<GradientStop Color="#FFFFAAAA"/>
<GradientStop x:Name="backgroundColor" Color="Red" Offset="1.2"/>
</RadialGradientBrush>
</Border.Background>
<Border.BorderBrush>
<RadialGradientBrush>
<GradientStop x:Name="GradientColorLow" Color="#FF660000" Offset="0.383"/>
<GradientStop x:Name="GradientColorHigh" Color="#330000" Offset="0.5"/>
</RadialGradientBrush>
</Border.BorderBrush>
</Border>
<Label Content="{Binding Text}" x:Name="LEDText" Foreground="Red" HorizontalContentAlignment="Left" VerticalContentAlignment="Center"/>
</StackPanel>
</UserControl>
and the code behind:
public partial class LedControl : UserControl
{
#region Dependency properties
/// <summary>Dependency property to Get/Set the current IsActive (True/False)</summary>
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool?), typeof(LedControl),
new PropertyMetadata(null, new PropertyChangedCallback(LedControl.IsCheckedPropertyChanced)));
/// <summary>Dependency property to Get/Set Color when IsActive is true</summary>
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("Color", typeof(Color), typeof(LedControl),
new PropertyMetadata(Colors.Green, new PropertyChangedCallback(LedControl.OnColorPropertyChanged)));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(LedControl),
new PropertyMetadata("ButtonText", new PropertyChangedCallback(LedControl.OnTextPropertyChanged)));
#endregion
#region Properties
/// <summary>Gets/Sets Text Value</summary>
public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }
/// <summary>Gets/Sets Value</summary>
public bool? IsChecked { get { return (bool?)GetValue(IsCheckedProperty); } set { SetValue(IsCheckedProperty, value); } }
/// <summary>Gets/Sets Color</summary>
public Color Color { get { return (Color)GetValue(ColorProperty); } set { SetValue(ColorProperty, value); } }
#endregion
#region Constructor
public LedControl()
{
InitializeComponent();
if (this.IsChecked == true)
{
this.LEDColor.Color = this.Color;
this.LEDText.Foreground = new SolidColorBrush(this.Color);
}
else if (this.IsChecked == false)
{
this.LEDColor.Color = Colors.Gray;
this.LEDText.Foreground = new SolidColorBrush(Colors.Gray);
}
}
#endregion
#region Callbacks
private static void IsCheckedPropertyChanced(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LedControl led = (LedControl)d;
if (led.IsChecked == true)
{
led.LEDColor.Color = led.Color;
led.LEDText.Foreground = new SolidColorBrush(led.Color);
}
else
{
led.LEDColor.Color = Colors.Gray; // TODO calculate dark/gray color
led.LEDText.Foreground = new SolidColorBrush(Colors.Gray);
}
}
private static void OnColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LedControl led = (LedControl)d;
led.Color = (Color)e.NewValue;
if (led.IsChecked == true)
{
led.LEDColor.Color = led.Color;
led.LEDText.Foreground = new SolidColorBrush( led.Color );
}
}
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LedControl led = (LedControl)d;
led.Text = (String)e.NewValue;
}
#endregion
}
The thing is that the control does not work. I set the Color to forrestGreen, but shows up red in designer and if I execute the program:
The text "Some Option" is not shown as well..
I haven't figured out how to have the gradient colors to be darker and lighter versions of the color I want.
The look of the led is also not as cool as in winforms,
but I have no clue to translate the code to wpf.
here is the part of the code that draws the led in win-Forms:
private void drawControl(Graphics g, bool on) {
// Is the bulb on or off
Color lightColor = (on) ? this.Color : Color.FromArgb(100, this.Color);
Color darkColor = (on) ? this.DarkColor : Color.Gray/*this.DarkDarkColor*/;
// Calculate the dimensions of the bulb
int width = this.Width - (this.Padding.Left + this.Padding.Right);
int height = this.Height - (this.Padding.Top + this.Padding.Bottom);
// Diameter is the lesser of width and height
int diameter = Math.Min(width, height);
// Subtract 1 pixel so ellipse doesn't get cut off
diameter = Math.Max(diameter - 1, 1);
SolidBrush br = new SolidBrush(BackColor);
g.FillRectangle(br, ClientRectangle);
// Draw the background ellipse
var rectangle = new Rectangle(this.Padding.Left, this.Padding.Top, diameter, diameter);
g.FillEllipse(new SolidBrush(darkColor), rectangle);
// Draw the glow gradient
var path = new GraphicsPath();
path.AddEllipse(rectangle);
var pathBrush = new PathGradientBrush(path);
pathBrush.CenterColor = lightColor;
pathBrush.SurroundColors = new Color[] { Color.FromArgb(0, lightColor) };
g.FillEllipse(pathBrush, rectangle);
// Draw the white reflection gradient
var offset = Convert.ToInt32(diameter * .15F);
var diameter1 = Convert.ToInt32(rectangle.Width * .8F);
var whiteRect = new Rectangle(rectangle.X - offset, rectangle.Y - offset, diameter1, diameter1);
var path1 = new GraphicsPath();
path1.AddEllipse(whiteRect);
var pathBrush1 = new PathGradientBrush(path);
pathBrush1.CenterColor = _reflectionColor;
pathBrush1.SurroundColors = _surroundColor;
g.FillEllipse(pathBrush1, whiteRect);
// Draw the border
g.SetClip(this.ClientRectangle);
if (this.On)
g.DrawEllipse(new Pen(Color.FromArgb(85, Color.Black),1F), rectangle);
if (this.Text != string.Empty)
{
RectangleF textArea = this.ClientRectangle;
textArea.X += rectangle.Width + 6;
textArea.Width -= (diameter + 6);
Font fon = new Font(Font.FontFamily, Font.Size-1, FontStyle.Bold);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Center;
if (!this.On)
g.DrawString(this.Text, fon, new SolidBrush(Color.Gray), textArea, sf);
else
g.DrawString(this.Text, fon, new SolidBrush(darkColor), textArea, sf);
}
}
My second try with the checkbox as base is nore or less useless, but perhaps someone is keen and can substitute the checkbox with the led.
Any help is appreciated!
here is a LedControl derived from CheckBox. LedControl itself adds OnColor and OffColor properties.
public class LedControl : CheckBox
{
static LedControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LedControl), new FrameworkPropertyMetadata(typeof(LedControl)));
}
public static readonly DependencyProperty OnColorProperty =
DependencyProperty.Register("OnColor", typeof(Brush), typeof(LedControl), new PropertyMetadata(Brushes.Green));
public Brush OnColor
{
get { return (Brush)GetValue(OnColorProperty); }
set { SetValue(OnColorProperty, value); }
}
public static readonly DependencyProperty OffColorProperty =
DependencyProperty.Register("OffColor", typeof(Brush), typeof(LedControl), new PropertyMetadata(Brushes.Red));
public Brush OffColor
{
get { return (Brush)GetValue(OffColorProperty); }
set { SetValue(OffColorProperty, value); }
}
}
and visual appearance is customized via Style and Template. Main template parts are LedBorder ellipse, white CenterGlow ellipse, white CornerLight shape and of course ContentPresent. LedBorder adapts to LedControl height. Depending on IsChecked LedBorder is colored with OnColor or OffColor (as well as Foreground). Disabled control is grayed.
<Style TargetType="local:LedControl">
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LedControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Background="Transparent" Name="grd"
Margin="{TemplateBinding Padding}"
VerticalAlignment="Stretch"
Width="{Binding Path=ActualHeight, Mode=OneWay, RelativeSource={RelativeSource Self}}">
<Ellipse x:Name="LedBorder"
Fill="{TemplateBinding Background}"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="2"
Stretch="Uniform"/>
<Ellipse x:Name="CenterGlow" Stretch="Uniform">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="White" Offset="-0.25"/>
<GradientStop Color="Transparent" Offset="0.91"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse x:Name="CornerLight" Stretch="Uniform" Margin="2">
<Ellipse.Fill>
<RadialGradientBrush Center="0.15 0.15" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Transparent" Offset="1"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
<ContentPresenter x:Name="content" Grid.Column="1" Margin="4,0,0,0"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="LedBorder" Property="Fill" Value="{Binding Path=OnColor, RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter TargetName="content" Property="TextElement.Foreground" Value="{Binding Path=OnColor, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="LedBorder" Property="Fill" Value="{Binding Path=OffColor, RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter TargetName="content" Property="TextElement.Foreground" Value="{Binding Path=OffColor, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="CenterGlow" Property="Fill">
<Setter.Value>
<RadialGradientBrush Opacity="1">
<GradientStop Color="Transparent" Offset="-0.5" />
<GradientStop Color="#888" Offset="1" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
<Setter TargetName="content" Property="TextElement.Foreground" Value="#888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and here is a sample:
<StackPanel>
<local:LedControl Content="Disabled OFF" Height="24" IsChecked="False" IsEnabled="False" />
<local:LedControl Content="Disabled ON" Height="32" IsChecked="True" IsEnabled="False" />
<local:LedControl Content="Enabled OFF" OffColor="Chocolate" IsChecked="False" Height="40" />
<local:LedControl Content="Enabled ON" OnColor="Navy" IsChecked="True" Height="48" />
</StackPanel>
You can use PathGradientBrush to draw a radial gradient. Here is the result of the code which I wrote. You can use any color as CheckedColor and UnCheckedColor, I used Red and Green to get this result:
Code
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public class MyCheckBox : CheckBox
{
public MyCheckBox()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.DoubleBuffered = true;
this.ResizeRedraw = true;
CheckedColor = Color.Green; ;
UnCheckedColor = Color.Red; ;
}
[DefaultValue(typeof(Color), "Green")]
public Color CheckedColor { get; set; }
[DefaultValue(typeof(Color), "Red")]
public Color UnCheckedColor { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
var darkColor = Color.Black;
var lightColor = Color.FromArgb(200, Color.White);
var cornerAlpha = 80;
this.OnPaintBackground(e);
using (var path = new GraphicsPath())
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var rect = new Rectangle(0, 0, Height, Height);
path.AddEllipse(rect);
rect.Inflate(-1, -1);
using (var bgBrush = new SolidBrush(darkColor))
{
e.Graphics.FillEllipse(bgBrush, rect);
}
using (var pathGrBrush = new PathGradientBrush(path))
{
var color = Checked ? CheckedColor : UnCheckedColor;
pathGrBrush.CenterColor = color; ;
Color[] colors = { Color.FromArgb(cornerAlpha, color) };
pathGrBrush.SurroundColors = colors;
e.Graphics.FillEllipse(pathGrBrush, rect);
}
using (var pathGrBrush = new PathGradientBrush(path))
{
pathGrBrush.CenterColor = lightColor; ;
Color[] colors = { Color.Transparent };
pathGrBrush.SurroundColors = colors;
var r = (float)(Math.Sqrt(2) * Height / 2);
var x = r / 8;
e.Graphics.FillEllipse(pathGrBrush, new RectangleF(-x, -x, r, r));
e.Graphics.ResetClip();
}
}
TextRenderer.DrawText(e.Graphics, Text, Font,
new Rectangle(Height, 0, Width - Height, Height), ForeColor,
TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
}
}
Related
I created a datagrid. I would like to include a custom control in a DataTemplate (CLightSystem). The control works outside of the data grid. In the data grid, the "Value" property is always empty, although StatusDrawing is not empty. What could be the reason for this?
<DataGrid Grid.Row="1" Grid.Column="5"
BorderThickness="3"
ItemsSource="{Binding CncCollectionM12}"
SelectedItem="{Binding SelectedItem}"
AutoGenerateColumns="False"
CanUserAddRows="False"
IsReadOnly="True"
FontSize="16"
Style="{StaticResource MaterialDesignDataGrid}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="M12" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="LIGHT SYSTEM"></TextBlock>
<components:CLightSystem Value="{Binding StatusDrawing}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
This is the class from the custom control:
public class CLightSystem : Control
{
private Ellipse redLight;
private Ellipse orangeLight;
private Ellipse greenLight;
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(string), typeof(CLightSystem), new PropertyMetadata(default(string)));
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
static CLightSystem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CLightSystem), new FrameworkPropertyMetadata(typeof(CLightSystem)));
}
public override void OnApplyTemplate()
{
redLight = Template.FindName("ERedLight", this) as Ellipse;
orangeLight = Template.FindName("EOrangeLight", this) as Ellipse;
greenLight = Template.FindName("EGreenLight", this) as Ellipse;
UpdateLight();
base.OnApplyTemplate();
}
public void UpdateLight()
{
switch (Value)
{
case "GESPERRT":
redLight.Fill = new SolidColorBrush(Colors.Red);
redLight.Effect = new DropShadowEffect()
{
Color = new Color { A = 255, R = 255, G = 0, B = 0 },
BlurRadius = 3,
Direction = -90,
ShadowDepth = 0
};
break;
case "IN BEARBEITUNG":
orangeLight.Fill = new SolidColorBrush(Colors.DarkOrange);
orangeLight.Effect = new DropShadowEffect()
{
Color = new Color { A = 255, R = 255, G = 165, B = 255 },
BlurRadius = 3,
Direction = -90,
ShadowDepth = 0
};
break;
case "FREIGEGEBEN":
greenLight.Fill = new SolidColorBrush(Colors.Green);
greenLight.Effect = new DropShadowEffect()
{
Color = new Color { A = 255, R = 0, G = 255, B = 0 },
BlurRadius = 3,
Direction = -90,
ShadowDepth = 0
};
break;
}
}
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Components/DLightSystemStyle.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:components="clr-namespace:Pruefprogramm.Components">
<Style TargetType="components:CLightSystem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="components:CLightSystem">
<Border BorderBrush="Black" BorderThickness="1">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal" Background="DimGray">
<Ellipse Height="20"
Width="20"
Fill="LightGray"
x:Name="ERedLight"/>
<Ellipse Height="20"
Width="20"
Fill="LightGray"
x:Name="EOrangeLight" />
<Ellipse Height="20"
Width="20"
Fill="LightGray"
x:Name="EGreenLight" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Thank you very much
Levin
If it works outside the DataGrid in the same parent control/window but not inside, it's probably the binding to StatusDrawing that fails.
If this property is defined in the same class as the CncCollectionM12 property, you should bind to it like this:
<components:CLightSystem Value="{Binding DataContext.StatusDrawing,
RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
I am writing a custom user control library in .NET 4.5.2 with WPF.
My custom Button behaves very strange and I am really desperate by now. First of all, here's the project GitHub
The Problem
Here is the MainWindow of a test-project with the custom Button
Click-Event Code
Window1 win1 = new Window1();
win1.ShowDialog();
XAML-Code
<Grid>
<Cosmos:CButton x:Name="B1"
C_BackgroundTheme="HKS41"
HorizontalAlignment="Left"
Margin="62,80,0,0"
VerticalAlignment="Top"
Width="117"
Content="ShowDialog"
Click="CButton_Click"/>
</Grid>
Nothing too special. Now I click the Button and the second Window opens as usually.
XAML-Code
<Grid>
<Cosmos:CButton C_BackgroundTheme="Red" HorizontalAlignment="Left" Margin="83,100,0,0" VerticalAlignment="Top" Width="75" Content="Test 2"/>
<Cosmos:CButton C_BackgroundTheme="HKS44" HorizontalAlignment="Left" Margin="83,63,0,0" VerticalAlignment="Top" Width="75" Content="Test"/>
</Grid>
The Button from which the ShowDialog goes out, changes its Backgroundcolor to the one of the last button in the XAML code. In this case the lighter blue (HSK44), not the red.
Code
This is the CS-Code of the Custom Button:
public class CButton : Button
{
public static readonly DependencyProperty BackgroundThemeProperty = DependencyProperty.Register("C_BackgroundTheme", typeof(Theme), typeof(CButton), new PropertyMetadata(new PropertyChangedCallback(BackgroundValueChanged)));
public static readonly DependencyProperty ForegroundThemeProperty = DependencyProperty.Register("C_ForegroundTheme", typeof(Theme), typeof(CButton), new PropertyMetadata(new PropertyChangedCallback(ForegroundValueChanged)));
public static bool Backgroundchanged = false;
private static string backcolor;
private static string Backcolor
{
get { return backcolor; }
set
{
backcolor = value;
Backgroundchanged = true;
}
}
static CButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CButton), new FrameworkPropertyMetadata(typeof(CButton)));
}
public Theme C_BackgroundTheme
{
get
{
return (Theme)GetValue(BackgroundThemeProperty);
}
set
{
SetValue(BackgroundThemeProperty, value);
}
}
public Theme C_ForegroundTheme
{
get
{
return (Theme)GetValue(ForegroundThemeProperty);
}
set
{
SetValue(ForegroundThemeProperty, value);
}
}
private static void BackgroundValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (CButton)d;
Backcolor = GetColorString((Theme)e.NewValue);
control.Background = (SolidColorBrush)(new BrushConverter().ConvertFrom(Backcolor));
control.OpacityMask = (SolidColorBrush)(new BrushConverter().ConvertFrom(ChangeColorBrightness((Color)ColorConverter.ConvertFromString(Backcolor), (float)-0.3).ToString()));
}
private static void ForegroundValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (CButton)d;
control.Foreground = GetColorBrush((Theme)e.NewValue);
}
public static Color ChangeColorBrightness(Color color, float correctionFactor)
{
float red = (float)color.R;
float green = (float)color.G;
float blue = (float)color.B;
if (correctionFactor < 0)
{
correctionFactor = 1 + correctionFactor;
red *= correctionFactor;
green *= correctionFactor;
blue *= correctionFactor;
}
else
{
red = (255 - red) * correctionFactor + red;
green = (255 - green) * correctionFactor + green;
blue = (255 - blue) * correctionFactor + blue;
}
return Color.FromArgb(color.A, (byte)red, (byte)green, (byte)blue);
}
protected override void OnMouseEnter(MouseEventArgs e)
{
Backcolor = this.Background.ToString();
ColorAnimation colorChangeAnimation = new ColorAnimation
{
To = ChangeColorBrightness((Color)ColorConverter.ConvertFromString(Backcolor), (float)0.15),
Duration = new Duration(new TimeSpan(0, 0, 0, 0, 100))
};
PropertyPath colorTargetPath = new PropertyPath("(Background).(SolidColorBrush.Color)");
Storyboard CellBackgroundChangeStory = new Storyboard();
Storyboard.SetTarget(colorChangeAnimation, this);
Storyboard.SetTargetProperty(colorChangeAnimation, colorTargetPath);
CellBackgroundChangeStory.Children.Add(colorChangeAnimation);
CellBackgroundChangeStory.Begin();
}
protected override void OnMouseLeave(MouseEventArgs e)
{
ColorAnimation colorChangeAnimation = new ColorAnimation
{
To = (Color)ColorConverter.ConvertFromString(Backcolor),
Duration = new Duration(new TimeSpan(0, 0, 0, 0, 100))
};
PropertyPath colorTargetPath = new PropertyPath("(Background).(SolidColorBrush.Color)");
Storyboard CellBackgroundChangeStory = new Storyboard();
Storyboard.SetTarget(colorChangeAnimation, this);
Storyboard.SetTargetProperty(colorChangeAnimation, colorTargetPath);
CellBackgroundChangeStory.Children.Add(colorChangeAnimation);
CellBackgroundChangeStory.Begin();
}
protected override void OnClick()
{
Thread.Sleep(10);
base.OnClick();
}
}
And here is the ResourceDictionary
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Cosmos">
<Style TargetType="{x:Type local:CButton}">
<Setter Property="FontFamily" Value="/Cosmos;component/Fonts/#Circular Std Medium"/>
<Setter Property="Content" Value="CButton"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Foreground" Value="#FFEEEEEE"/>
<Setter Property="Background" Value="#FF111111"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Width" Value="120"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Border Name="outerborder"
BorderThickness="0,0,0,3"
BorderBrush="{TemplateBinding OpacityMask}"
CornerRadius="2"
Height="{TemplateBinding Height}">
<Border Name="outerbordertop"
BorderThickness="0,0,0,0"
BorderBrush="Transparent"
CornerRadius="2"
Height="{TemplateBinding Height}">
<Border Name="border"
BorderThickness="0,0,0,0"
CornerRadius="2"
Background="{TemplateBinding Background}"
VerticalAlignment="Stretch">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,1,0,0"/>
</Border>
</Border>
</Border>
<Rectangle Fill="{TemplateBinding OpacityMask}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,3,0,0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="0:0:0.07" FillBehavior="HoldEnd" Storyboard.TargetName="outerborder" Storyboard.TargetProperty="BorderThickness" To="0,0,0,0" />
<ThicknessAnimation Duration="0:0:0.07" FillBehavior="HoldEnd" Storyboard.TargetName="outerbordertop" Storyboard.TargetProperty="BorderThickness" To="0,3,0,0" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="0:0:0.07" FillBehavior="HoldEnd" Storyboard.TargetName="outerborder" Storyboard.TargetProperty="BorderThickness" To="0,0,0,3" />
<ThicknessAnimation Duration="0:0:0.07" FillBehavior="HoldEnd" Storyboard.TargetName="outerbordertop" Storyboard.TargetProperty="BorderThickness" To="0,0,0,0" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Why does the Button behave like this? I am also happy about any tips related to the Code.
Areo Glass Effect and windowStyle set to none and AllowTransparency causes widow resize to not function properly
After adding the Areo Glass theme to my window and setting WindowState to None the window does not resize properly. I can go as large as I want but when resizing down smaller the glass effect stays the same width and height.
For example I click Maximize. The window Expands to the full screen size. But when restoring my window restores back down but not the glass effect.
All I really want is the blur effect. I Don't care about the glass, but this seems to be the only way I can get a transparent blur.
How can I fix this?
xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" AllowsTransparency="True" WindowStyle="None" ResizeMode="CanResizeWithGrip" Background="Transparent">
<Border BorderThickness="1" Margin="0,35,0,0" BorderBrush="Orange">
<Grid Background="#0CFFA500">
<DockPanel VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="35" Background="#4effffff">
<DockPanel.Resources>
<Style x:Key="WindowIconStyle" TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="16" />
<Setter Property="Foreground" Value="#444" />
<Setter Property="Height" Value="30"></Setter>
<Setter Property="Width" Value="30"></Setter>
<Setter Property="FontFamily" Value="Webdings"></Setter>
<Setter Property="Background" Value="#ff0000"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Transparent" BorderThickness="0.5,0,0.5,0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#77abff" />
<Setter Property="Foreground" Value="#000000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Black" BorderThickness="1,0,1,0">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="White" Offset="0.952"/>
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FFF7F7F7" Offset="0.044"/>
</LinearGradientBrush>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="#444" BorderThickness="1,0,1,0">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
<GradientStop Color="#444" Offset="1"/>
<GradientStop Color="#ffececec" Offset="0.952"/>
<GradientStop Color="#444" Offset="0"/>
<GradientStop Color="#ffececec" Offset="0.044"/>
</LinearGradientBrush>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</DockPanel.Resources>
<Button DockPanel.Dock="Right" VerticalAlignment="Center" HorizontalAlignment="Right" Click="TriggerClose" Style="{StaticResource WindowIconStyle}" Content="r" />
<Button x:Name="btnMaximize" DockPanel.Dock="Right" VerticalAlignment="Center" HorizontalAlignment="Right" Click="TriggerMaximize" Style="{StaticResource WindowIconStyle}" Content="c" />
<Button DockPanel.Dock="Right" VerticalAlignment="Center" HorizontalAlignment="Right" Click="TriggerMinimize" Style="{StaticResource WindowIconStyle}" Content="0" />
<StatusBar Background="Transparent" MouseDoubleClick="TriggerMaximize" MouseMove="TriggerMoveWindow" >
<TextBlock DockPanel.Dock="Left" x:Name="txtTitle" Text="Title" FontSize="16" Padding="10,0,0,0"/>
</StatusBar>
</DockPanel>
<Border BorderThickness="0,1,0,0" Margin="0,35,0,0" BorderBrush="#f7000000">
</Border>
</Grid>
</Border>
</Window>
C#
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
namespace WpfApplication2 {
public partial class MainWindow : Window {
#region Constants
private System.Windows.Window me { get; set; }
private const int WM_DWMCOMPOSITIONCHANGED = 0x031E;
private const int DWM_BB_ENABLE = 0x1;
#endregion //Constants
#region Structures
[StructLayout(LayoutKind.Sequential)]
private struct DWM_BLURBEHIND {
public int dwFlags;
public bool fEnable;
public IntPtr hRgnBlur;
public bool fTransitionOnMaximized;
}
[StructLayout(LayoutKind.Sequential)]
private struct MARGINS {
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
#endregion //Structures
#region APIs
[DllImport("dwmapi.dll", PreserveSig = false)]
private static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);
[DllImport("dwmapi.dll")]
private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMargins);
[DllImport("dwmapi.dll", PreserveSig = false)]
private static extern bool DwmIsCompositionEnabled();
#endregion //APIs
public MainWindow() {
SourceInitialized += OnSourceInitialized;
InitializeComponent();
}
private void OnSourceInitialized(object sender, EventArgs eventArgs) {
me = (Window)sender;
if (Environment.OSVersion.Version.Major >= 6) {
var hwnd = new WindowInteropHelper(me).Handle;
var hs = HwndSource.FromHwnd(hwnd);
hs.CompositionTarget.BackgroundColor = System.Windows.Media.Colors.Transparent;
hs.AddHook(new HwndSourceHook(this.WndProc));
this.InitializeGlass(hwnd);
}
}
public System.Windows.Media.Color GetAreoColor() {
int icolor = (int)Microsoft.Win32.Registry.GetValue(#"HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM", "ColorizationColor", null);
var c = System.Drawing.Color.FromArgb(icolor);
return System.Windows.Media.Color.FromArgb(c.A, c.R, c.G, c.B);
}
private static String ConverterToHex(System.Drawing.Color c) {
return String.Format("#{0}{1}{2}", c.R.ToString("X2"), c.G.ToString("X2"), c.B.ToString("X2"));
}
#region Methods
#region InitializeGlass
public void InitializeGlass(IntPtr hwnd) {
if (!DwmIsCompositionEnabled())
return;
// fill the background with glass
var margins = new MARGINS();
margins.cxLeftWidth = margins.cxRightWidth = margins.cyBottomHeight = margins.cyTopHeight = -1;
DwmExtendFrameIntoClientArea(hwnd, ref margins);
// initialize blur for the window
DWM_BLURBEHIND bbh = new DWM_BLURBEHIND();
bbh.fEnable = true;
// bbh.fTransitionOnMaximized = true;
bbh.dwFlags = DWM_BB_ENABLE;
DwmEnableBlurBehindWindow(hwnd, ref bbh);
}
#endregion //InitializeGlass
#region WndProc
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (msg == WM_DWMCOMPOSITIONCHANGED) {
this.InitializeGlass(hwnd);
handled = false;
}
return IntPtr.Zero;
}
#endregion //WndProc
#endregion //Methods
private void TriggerMoveWindow(object sender, MouseEventArgs e) {
if (e.LeftButton == MouseButtonState.Pressed) {
if (WindowState == System.Windows.WindowState.Maximized) {
WindowState = System.Windows.WindowState.Normal;
double pct = PointToScreen(e.GetPosition(this)).X / System.Windows.SystemParameters.PrimaryScreenWidth;
Top = 0;
Left = e.GetPosition(this).X - (pct * Width);
}
Application.Current.MainWindow.DragMove();
}
}
private void TriggerMaximize(object sender, EventArgs e) {
if (WindowState == System.Windows.WindowState.Maximized) {
WindowState = System.Windows.WindowState.Normal;
btnMaximize.FontFamily = new System.Windows.Media.FontFamily("Webdings");
btnMaximize.Content = "c";
} else if (WindowState == System.Windows.WindowState.Normal) {
WindowState = System.Windows.WindowState.Maximized;
btnMaximize.FontFamily = new System.Windows.Media.FontFamily("Wingdings");
btnMaximize.Content = "r";
InvalidateVisual();
}
}
private void TriggerClose(object sender, RoutedEventArgs e) {
Close();
}
private void TriggerMinimize(object sender, RoutedEventArgs e) {
WindowState = System.Windows.WindowState.Minimized;
}
}
}
It seems that AllowsTransparency == True is the source of this behavior. Therefore you can remove it. However, you should set the Opacity of Backgroud Brush to be zero:
<Window.Background>
<SolidColorBrush Opacity="0" Color="Transparent"/>
</Window.Background>
This along with setting CompositionTarget.BackgroundColor to be transparent, and also using DwmEnableBlurBehindWindow function will make a similar result to setting AllowsTransparency == True.
However, with these modifications, the look of the window is not very pleasant because of its border. The Border will go away if you set ResizeMode=="NoResize". However, in this case, you should implement resizing yourself. Fortunately, this is easy by adding a Thumb and handling DragDelta event. Here is the full code:
Xaml
<Window x:Class="Test1.MainWindow"
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"
xmlns:local="clr-namespace:Test1"
mc:Ignorable="d"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Name="window"
Height="350" Width="525" WindowStyle="None" ResizeMode="NoResize" >
<Window.Background>
<SolidColorBrush Opacity="0" Color="Transparent"/>
</Window.Background>
<Grid>
<Thumb DockPanel.Dock="Bottom" DragDelta="Thumb_DragDelta" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="8" Height="8" Cursor="SizeNWSE" Background="Red" Panel.ZIndex="4"/>
<Border BorderThickness="1" BorderBrush="Orange" Margin="0,0,0,0">
<Grid Background="#0CFFA500">
<DockPanel VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="35" Background="#4effffff">
<DockPanel.Resources>
<Style x:Key="WindowIconStyle" TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="16" />
<Setter Property="Foreground" Value="#444" />
<Setter Property="Height" Value="30"></Setter>
<Setter Property="Width" Value="30"></Setter>
<Setter Property="FontFamily" Value="Webdings"></Setter>
<Setter Property="Background" Value="#ff0000"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Transparent" BorderThickness="0.5,0,0.5,0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#77abff" />
<Setter Property="Foreground" Value="#000000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Black" BorderThickness="1,0,1,0">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="White" Offset="0.952"/>
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FFF7F7F7" Offset="0.044"/>
</LinearGradientBrush>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="#444" BorderThickness="1,0,1,0">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
<GradientStop Color="#444" Offset="1"/>
<GradientStop Color="#ffececec" Offset="0.952"/>
<GradientStop Color="#444" Offset="0"/>
<GradientStop Color="#ffececec" Offset="0.044"/>
</LinearGradientBrush>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</DockPanel.Resources>
<Button DockPanel.Dock="Right" VerticalAlignment="Center" HorizontalAlignment="Right" Click="TriggerClose" Style="{StaticResource WindowIconStyle}" Content="r" />
<Button x:Name="btnMaximize" DockPanel.Dock="Right" VerticalAlignment="Center" HorizontalAlignment="Right" Click="TriggerMaximize" Style="{StaticResource WindowIconStyle}" Content="c" />
<Button DockPanel.Dock="Right" VerticalAlignment="Center" HorizontalAlignment="Right" Click="TriggerMinimize" Style="{StaticResource WindowIconStyle}" Content="0" />
<StatusBar Background="Transparent" MouseDoubleClick="TriggerMaximize" MouseMove="TriggerMoveWindow" >
<TextBlock DockPanel.Dock="Left" x:Name="txtTitle" Text="Title" FontSize="16" Padding="10,0,0,0"/>
</StatusBar>
</DockPanel>
<Border BorderThickness="0,1,0,0" Margin="0,35,0,0" BorderBrush="#f7000000">
</Border>
</Grid>
</Border>
</Grid>
</Window>
CS Code
public partial class MainWindow : Window
{
private const int WM_DWMCOMPOSITIONCHANGED = 0x031E;
private const int DWM_BB_ENABLE = 0x1;
[StructLayout(LayoutKind.Sequential)]
private struct DWM_BLURBEHIND
{
public int dwFlags;
public bool fEnable;
public IntPtr hRgnBlur;
public bool fTransitionOnMaximized;
}
[StructLayout(LayoutKind.Sequential)]
private struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
[DllImport("dwmapi.dll", PreserveSig = false)]
private static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);
[DllImport("dwmapi.dll")]
private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMargins);
[DllImport("dwmapi.dll", PreserveSig = false)]
private static extern bool DwmIsCompositionEnabled();
public MainWindow()
{
InitializeComponent();
SourceInitialized += OnSourceInitialized;
}
private void OnSourceInitialized(object sender, EventArgs eventArgs)
{
var hwnd = new WindowInteropHelper(this).Handle;
var hs = HwndSource.FromHwnd(hwnd);
hs.CompositionTarget.BackgroundColor = System.Windows.Media.Colors.Transparent;
var margins = new MARGINS();
margins.cxLeftWidth = margins.cxRightWidth = margins.cyTopHeight = margins.cyBottomHeight = -1;
DwmExtendFrameIntoClientArea(hwnd, ref margins);
DWM_BLURBEHIND bbh = new DWM_BLURBEHIND();
bbh.fEnable = true;
bbh.dwFlags = DWM_BB_ENABLE;
DwmEnableBlurBehindWindow(hwnd, ref bbh);
}
private void TriggerMinimize(object sender, RoutedEventArgs e)
{
WindowState = System.Windows.WindowState.Minimized;
}
private void TriggerMoveWindow(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (WindowState == System.Windows.WindowState.Maximized)
{
WindowState = System.Windows.WindowState.Normal;
double pct = PointToScreen(e.GetPosition(this)).X / System.Windows.SystemParameters.PrimaryScreenWidth;
Top = 0;
Left = e.GetPosition(this).X - (pct * Width);
}
Application.Current.MainWindow.DragMove();
}
}
private void TriggerMaximize(object sender, EventArgs e)
{
if (WindowState == System.Windows.WindowState.Maximized)
{
WindowState = System.Windows.WindowState.Normal;
btnMaximize.FontFamily = new System.Windows.Media.FontFamily("Webdings");
btnMaximize.Content = "c";
}
else if (WindowState == System.Windows.WindowState.Normal)
{
WindowState = System.Windows.WindowState.Maximized;
btnMaximize.FontFamily = new System.Windows.Media.FontFamily("Wingdings");
btnMaximize.Content = "r";
InvalidateVisual();
}
}
private void TriggerClose(object sender, RoutedEventArgs e)
{
Close();
}
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
this.Height += e.VerticalChange;
this.Width += e.HorizontalChange;
}
}
You can see the result:
I'm new here so please excuse my if I missed to add something needed to answer my question.
So heres my question:
I am trying to add shapes to a canvas while also wanting to show a list of them in a listbox, to make them changeable (size,position, etc.). I am using WPF. Is there a way to do so?
And if it doesnt bother you: Is there maybe a question or website or whatever about how to dynamically draw shapes(circle,ellipse,rect, etc.) with mouse events?
I hope you can help me. Thanks in advance.
Edit:
Given the fact that I have:
public partial class MainWindow : Window
{
public ObservableCollection<string> Baselist = new ObservableCollection<string>();
public ObservableCollection<string> Crystallist = new ObservableCollection<string>();
public ObservableCollection<Shape> Shapelist = new ObservableCollection<Shape>();
public MainWindow()
{
this.ResizeMode = System.Windows.ResizeMode.CanMinimize;
InitializeComponent();
InitializeLists(Baseforms,CrystalGroups);
}
private void InitializeLists(ComboBox Baseforms, ComboBox CrystalGroups)
{
Baseforms.ItemsSource = Baselist;
CrystalGroups.ItemsSource = Crystallist;
Shape Circle = new Ellipse();
Circle.Stroke = System.Windows.Media.Brushes.Black;
Circle.Fill = System.Windows.Media.Brushes.DarkBlue;
Circle.HorizontalAlignment = HorizontalAlignment.Left;
Circle.VerticalAlignment = VerticalAlignment.Center;
Circle.Width = 50;
Circle.Height = 50;
Shapelist.Add(Circle);
}
How can I use an ItemsControl to show the shapes in Shapelist in an canvas while also listing them in a Listbox?
Hope this makes the question less broad.
Please try the next solution:
Updated version (xaml and code behind)
DetailsList list view - presents detailed data based on ShapeDataPresentation class (supports multi-select). Shows data by data template named ShapeDataPresentationDataTemplate.
ShapesPresentor items control - presents shapes on canvas (doesn't support multi-select only one can be selected).
ListView XAML code ("This" is the name of the ListView containing window)
<Window x:Class="ListViewWithCanvasPanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:listViewWithCanvasPanel="clr-namespace:ListViewWithCanvasPanel"
Title="MainWindow" Height="350" Width="525" x:Name="This" ResizeMode="CanResize">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2.5*"></ColumnDefinition>
<ColumnDefinition Width="4*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListView x:Name="DetailsList" Panel.ZIndex="999" Grid.Column="0" ItemsSource="{Binding ElementName=This, Path=Shapes}" SelectionMode="Extended" SelectionChanged="Selector_OnSelectionChanged">
<ListView.Resources>
<DataTemplate x:Key="ShapeDataPresentationDataTemplate" DataType="listViewWithCanvasPanel:ShapeDataPresentation">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name, StringFormat={}{0:N0}%}"></TextBlock>
<TextBlock Grid.Column="1">
<Run Text="W:"></Run>
<Run Text="{Binding OriginalRectAroundShape.Width}"></Run>
</TextBlock>
<TextBlock Grid.Column="2">
<Run Text="H:"></Run>
<Run Text="{Binding OriginalRectAroundShape.Height}"></Run>
</TextBlock>
</Grid>
</DataTemplate>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="Shape">
<ContentControl Content="{Binding Tag}" ContentTemplate="{StaticResource ShapeDataPresentationDataTemplate}"></ContentControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
<GridSplitter Grid.Column="0" Width="3" Background="Blue" Panel.ZIndex="999"
VerticalAlignment="Stretch" HorizontalAlignment="Right" Margin="0"/>
<ItemsControl Grid.Column="1" x:Name="ShapesPresentor" ItemsSource="{Binding ElementName=This, Path=Shapes}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
MouseDown="UIElement_OnMouseDown">
<!--<ListView.Resources>
<ControlTemplate x:Key="SelectedTemplate" TargetType="ListViewItem">
<ContentControl Content="{Binding }"></ContentControl>
</ControlTemplate>
</ListView.Resources>-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="White" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true" />
<Condition Property="Selector.IsSelectionActive" Value="true" />
</MultiTrigger.Conditions>
<Setter Property="Template" Value="{StaticResource SelectedTemplate}" />
</MultiTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>-->
</ItemsControl>
</Grid>
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal">
<ComboBox x:Name="Baseforms"></ComboBox>
<ComboBox x:Name="CrystalGroups"></ComboBox>
</StackPanel>
</Grid>
Code behind (updated)
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public static readonly DependencyProperty ShapesProperty = DependencyProperty.Register(
"Shapes", typeof (ObservableCollection<Shape>), typeof (MainWindow),
new PropertyMetadata(default(ObservableCollection<Shape>)));
public ObservableCollection<Shape> Shapes
{
get { return (ObservableCollection<Shape>) GetValue(ShapesProperty); }
set { SetValue(ShapesProperty, value); }
}
public ObservableCollection<string> Baselist = new ObservableCollection<string> {"a", "b", "c"};
public ObservableCollection<string> Crystallist = new ObservableCollection<string>{"aa", "bb", "cc"};
public ObservableCollection<Shape> Shapelist = new ObservableCollection<Shape>();
private SolidColorBrush _originalColorBrush = Brushes.Tomato;
private SolidColorBrush _selectedColorBrush;
private double _diameter;
public MainWindow()
{
_diameter = 50d;
this.ResizeMode = System.Windows.ResizeMode.CanMinimize;
Shapes = new ObservableCollection<Shape>();
InitializeComponent();
InitializeLists(Baseforms, CrystalGroups);
}
private void InitializeLists(ComboBox Baseforms, ComboBox CrystalGroups)
{
Baseforms.ItemsSource = Baselist;
CrystalGroups.ItemsSource = Crystallist;
Shape Circle = new Ellipse();
Circle.Stroke = System.Windows.Media.Brushes.Black;
Circle.Fill = System.Windows.Media.Brushes.DarkBlue;
Circle.HorizontalAlignment = HorizontalAlignment.Left;
Circle.VerticalAlignment = VerticalAlignment.Center;
Circle.Width = 50;
Circle.Height = 50;
Shapelist.Add(Circle);
}
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var inputElement = sender as IInputElement;
if (inputElement == null) return;
var point = e.GetPosition(inputElement);
Shape shape = new Ellipse
{
Stroke = Brushes.Black,
Fill = _originalColorBrush,
Width = _diameter,
Height = _diameter
};
var byX = point.X - _diameter / 2d;
var byY = point.Y - _diameter / 2d;
var existingShape = Shapes.FirstOrDefault(shapeToCheck =>
{
var data = shapeToCheck.Tag as ShapeDataPresentation;
if (data == null) return false;
var res = data.OriginalRectAroundShape.IntersectsWith(new Rect(point,point));
return res;
});
if (existingShape == null)
{
var shapeDataPresentation = new ShapeDataPresentation { Name = string.Format("Ox:{0}, Oy:{1}", point.X.ToString("##.###"), point.Y.ToString("##.###")), OriginalRectAroundShape = new Rect(new Point(byX, byY), new Size(_diameter, _diameter)) };
shape.Tag = shapeDataPresentation;
shape.ToolTip = new ToolTip{Content = shapeDataPresentation.Name};
var translateTransform = new TranslateTransform(byX, byY);
shape.RenderTransform = translateTransform;
Shapes.Add(shape);
}
else
{
if (DetailsList.SelectedItems.Contains(existingShape) == false)
{
DetailsList.SelectedItems.Clear();
DetailsList.SelectedItems.Add(existingShape);
}
}
}
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var currentSelected = e.AddedItems.OfType<Shape>().ToList();
var prevSelected = e.RemovedItems.OfType<Shape>().ToList();
if (currentSelected.Count > 0)
{
currentSelected.ForEach(shape =>
{
_selectedColorBrush = Brushes.CadetBlue;
shape.Fill = _selectedColorBrush;
});
}
if (prevSelected.Count > 0)
{
prevSelected.ForEach(shape =>
{
shape.Fill = _originalColorBrush;
});
}
}
}
public class ShapeDataPresentation
{
public string Name { get; set; }
public Rect OriginalRectAroundShape { get; set; }
}
How it is looks like
Summary:
Here you can create item by mouse click on canvas, mouse down handled
in code (UIElement_OnMouseDown).
Allowed selection and multi-selection, each time you make selection it will be handled in code (Selector_OnSelectionChanged).
But it is better to use the MVVM based approach to work in wpf.
Regards.
I have a listView with pages, and I try to sort it on column header click, but the problem is that it will sort only the items on the current page.I want the sorter to sort all the items and to display the items on pages in the right order. Example: now if I have 5,3,1 on page 1 and 2,6,4 on page 2 it will show 1,3,5 on page 1 and 2,4,6 on page2, and it needs to be 1,2,3 on page1 and 4,5,6 on page 2.
How I do it? This is the code I use:
CollectionViewSource view = new CollectionViewSource();
ObservableCollection<Server> Source = new ObservableCollection<Server>();
int currentPageIndex = 0;
int itemPerPage = 13;
int totalPage = 0;
//populating the listview
public void refreshList()
{
int itemcount = 0;
foreach (string id in server_id)
{
if (id != null && id != "")
{
Source.Add(new Server
{
ID = Int32.Parse(id),
NAME = server_name[Int32.Parse(id)],
PING = 0
});
itemcount++;
}
}
totalPage = itemcount / itemPerPage;
if (itemcount % itemPerPage != 0)
{
totalPage += 1;
}
view.Source = Source;
view.Filter += new FilterEventHandler(view_Filter);
listView1.DataContext = view;
}
//Paging
private void ShowCurrentPageIndex()
{
this.next.Content = (currentPageIndex + 1).ToString();
}
void view_Filter(object sender, FilterEventArgs e)
{
int index = Source.IndexOf((Server)e.Item);
if (index >= itemPerPage * currentPageIndex && index < itemPerPage * (currentPageIndex + 1))
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}
private void btnPrev_Click(object sender, RoutedEventArgs e)
{
// Display previous page
if (currentPageIndex > 0)
{
currentPageIndex--;
view.View.Refresh();
}
ShowCurrentPageIndex();
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
// Display next page
if (currentPageIndex < totalPage - 1)
{
currentPageIndex++;
view.View.Refresh();
}
ShowCurrentPageIndex();
}
//Sorting
GridViewColumnHeader _lastHeaderClicked = null;
ListSortDirection _lastDirection = ListSortDirection.Ascending;
void GridViewColumnHeaderClickedHandler(object sender, RoutedEventArgs e)
{
GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
ListSortDirection direction;
if (headerClicked != null)
{
if (headerClicked != _lastHeaderClicked)
{
direction = ListSortDirection.Ascending;
}
else
{
if (_lastDirection == ListSortDirection.Ascending)
{
direction = ListSortDirection.Descending;
}
else
{
direction = ListSortDirection.Ascending;
}
}
string header = headerClicked.Column.Header as string;
Sort(header, direction);
_lastHeaderClicked = headerClicked;
_lastDirection = direction;
}
}
private void Sort(string sortBy, ListSortDirection direction)
{
var sortProperty = typeof(Server).GetProperty(sortBy);
if (direction == ListSortDirection.Ascending)
{
Source = new ObservableCollection<Server>(Source.OrderBy(s => sortProperty.GetValue(s)));
}
else
{
Source = new ObservableCollection<Server>(Source.OrderByDescending(s => sortProperty.GetValue(s)));
}
view.Source = Source;
view.View.Refresh();
}
ListView XAML:
<ListView Name="listView1" ItemsSource="{Binding}" ItemContainerStyle="{StaticResource LVitem}" BorderBrush="{x:Null}" Height="502" Margin="10,130,10,0" FontSize="20" GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler" FontFamily="/WpfApplication2;component/Resources/#Purista SemiBold" Background="#66181F2C" BorderThickness="1.000001" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" >
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource ColumnHeader}" >
<GridViewColumn Header=" ID" Width="0" DisplayMemberBinding="{Binding ID}" />
<GridViewColumn Header=" NAME" Width="500" DisplayMemberBinding="{Binding NAME}" />
<GridViewColumn Header=" PING" Width="100" DisplayMemberBinding="{Binding PING}" />
</GridView>
</ListView.View>
</ListView>
<Button x:Name="next" Content="Next" Margin="1179,0,0,0" Click="btnNext_Click"/>
<Button x:Name="prev" Content="Prev" Margin="1179,0,0,0" Click="btnPrev_Click"/>
and this is the style used for listview:
<Style x:Key="LVitem" TargetType="{x:Type ListViewItem}">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#663F5768" Offset="0"/>
<GradientStop Color="#992E4051" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid SnapsToDevicePixels="true">
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0,1,0,1" Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="7"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle x:Name="UpperHighlight" Fill="#FFE3F7FF" Visibility="Collapsed"/>
<Border Padding="{TemplateBinding Padding}" Grid.RowSpan="2">
<GridViewRowPresenter x:Name="Content" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="0,0,0,1" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#66597B93" Offset="0"/>
<GradientStop Color="#99364B5F" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="{x:Null}"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFDADADA" Offset="1"/>
<GradientStop Color="White" Offset="0"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="{x:Null}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Because of the way you are filtering the items you have to sort your Source collection instead of the items.
private void Sort(string sortBy, ListSortDirection direction)
{
var sortProperty = typeof(Server).GetProperty(sortBy);
if(sortProperty == null) return;
if (direction == ListSortDirection.Ascending)
{
Source = new ObservableCollection<Server>(Source.OrderBy(s => sortProperty.GetValue(s, null)));
}
else
{
Source = new ObservableCollection<Server>(Source.OrderByDescending(s => sortProperty.GetValue(s, null)));
}
view.Source = Source;
view.View.Refresh();
}