I'm stuck and not sure why when the user drags the size-gripper, to resize the control on the canvas, that nothing appears to change. However it does fire and print to the console. What am I missing here that's causing it to not resize the item in wpf. I'm not doing anything really complex either. It's pretty straight forward. I'm assuming the issue is somewhere in the ResizeGizmo.cs. I'm guessing for some reason it's not letting me set the size of the contents of the item it's targeting.
Code
GraphNodeViewModel.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApplication1
{
public class NodeViewModel : NotifyBase
{
// position coordinates
private double x = 0;
public double X
{
get { return x; }
set { Set(ref x, value); }
}
private double y = 0;
public double Y
{
get { return y; }
set { Set(ref y, value); }
}
public double Z { get; set; }
private bool isSelected = false;
public bool IsSelected
{
get { return isSelected; }
set { Set(ref isSelected, value); }
}
private string virtualName = "New Node";
public string VirtualName
{
get { return virtualName; }
set { Set(ref virtualName, value); }
}
public NodeViewModel(string virtualName, double x, double y)
{
this.virtualName = virtualName;
this.X = x;
this.Y = y;
this.Z = 100; // place top most in z space
}
}
public abstract class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
RaisePropertyChanged(propertyName);
return true;
}
}
}
ResizeGizmo.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace WpfApplication1
{
public class ResizeGizmo : Thumb
{
public ResizeGizmo()
{
DragDelta += new DragDeltaEventHandler(this.ResizeGizmo_DragDelta);
}
private void ResizeGizmo_DragDelta(object sender, DragDeltaEventArgs e)
{
Console.WriteLine("Resizing");
Control item = this.DataContext as Control;
if (item != null)
{
double deltaVertical, deltaHorizontal;
switch (VerticalAlignment)
{
case VerticalAlignment.Bottom:
deltaVertical = Math.Min(-e.VerticalChange,
item.ActualHeight - item.MinHeight);
item.Height -= deltaVertical;
break;
case VerticalAlignment.Top:
deltaVertical = Math.Min(e.VerticalChange,
item.ActualHeight - item.MinHeight);
Canvas.SetTop(item, Canvas.GetTop(item) + deltaVertical);
item.Height -= deltaVertical;
break;
default:
break;
}
switch (HorizontalAlignment)
{
case HorizontalAlignment.Left:
deltaHorizontal = Math.Min(e.HorizontalChange,
item.ActualWidth - item.MinWidth);
Canvas.SetLeft(item, Canvas.GetLeft(item) + deltaHorizontal);
item.Width -= deltaHorizontal;
break;
case HorizontalAlignment.Right:
deltaHorizontal = Math.Min(-e.HorizontalChange,
item.ActualWidth - item.MinWidth);
item.Width -= deltaHorizontal;
break;
default:
break;
}
}
e.Handled = true;
}
}
}
MainWindow.xaml
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="700"
Background="sc#1,.01,.01,.01"
WindowStartupLocation="CenterScreen"
>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<Style x:Key="SizeGripStyle" TargetType="{x:Type Thumb}">
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
<Setter Property="Background" Value="#3FFFFFFF"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="1" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Resize Template -->
<ControlTemplate x:Key="ResizeGizmoTemplate" TargetType="Control">
<Grid>
<local:ResizeGizmo Height="1" Cursor="SizeNS" Margin="0 -4 0 0" VerticalAlignment="Top" HorizontalAlignment="Stretch" Style="{StaticResource SizeGripStyle}"/>
<local:ResizeGizmo Width="1" Cursor="SizeWE" Margin="-4 0 0 0" VerticalAlignment="Stretch" HorizontalAlignment="Left" Style="{StaticResource SizeGripStyle}"/>
<local:ResizeGizmo Width="1" Cursor="SizeWE" Margin="0 0 -4 0" VerticalAlignment="Stretch" HorizontalAlignment="Right" Style="{StaticResource SizeGripStyle}"/>
<local:ResizeGizmo Height="1" Cursor="SizeNS" Margin="0 0 0 -4" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Style="{StaticResource SizeGripStyle}"/>
<local:ResizeGizmo Width="6" Height="6" Cursor="SizeNWSE" Margin="-6 -6 0 0" VerticalAlignment="Top" HorizontalAlignment="Left" Style="{StaticResource SizeGripStyle}"/>
<local:ResizeGizmo Width="6" Height="6" Cursor="SizeNESW" Margin="0 -6 -6 0" VerticalAlignment="Top" HorizontalAlignment="Right" Style="{StaticResource SizeGripStyle}"/>
<local:ResizeGizmo Width="6" Height="6" Cursor="SizeNESW" Margin="-6 0 0 -6" VerticalAlignment="Bottom" HorizontalAlignment="Left" Style="{StaticResource SizeGripStyle}"/>
<local:ResizeGizmo Width="6" Height="6" Cursor="SizeNWSE" Margin="0 0 -6 -6" VerticalAlignment="Bottom" HorizontalAlignment="Right" Style="{StaticResource SizeGripStyle}"/>
</Grid>
</ControlTemplate>
<DataTemplate DataType="{x:Type local:NodeViewModel}">
<!--Outside border is used to visualize selection-->
<Border CornerRadius="4"
Padding="20"
BorderThickness="1"
BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=BorderBrush}"
Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=Background}">
<Grid>
<Border>
<Grid>
<Control Template="{StaticResource ResizeGizmoTemplate}"/>
<Border Background="LightBlue" CornerRadius="4" Padding="10">
<TextBox Text="This is cool"/>
</Border>
</Grid>
</Border>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<!-- visual Nodes and Connections -->
<ListBox x:Name="listBox" ItemsSource="{Binding GraphNodes}"
SelectionMode="Extended"
Background="Transparent">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" >
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
<Setter Property="Panel.ZIndex" Value="{Binding Z}" />
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter/>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="Background" Value="sc#0.3,1,1,1"/>
<Setter Property="BorderBrush" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.Resources>
</ListBox>
<!-- This Canvas is used to render a drag selection rectangle -->
<Canvas x:Name="dragSelectionCanvas" Visibility="Collapsed" >
<Border
x:Name="dragSelectionBorder"
BorderBrush="White"
BorderThickness="1"
Background="sc#0.1,1,1,1"
CornerRadius="2"
Opacity=".5"
IsHitTestVisible="False"
/>
</Canvas>
</Grid>
</Window>
MainWindowViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
public class MainWindowViewModel : NotifyBase
{
private ObservableCollection<NodeViewModel> graphNodes = new ObservableCollection<NodeViewModel>();
public ObservableCollection<NodeViewModel> GraphNodes { get { return graphNodes; } }
public MainWindowViewModel()
{
// Populate the view model with some example data.
graphNodes.Add(new NodeViewModel("Type here", 50, 50));
graphNodes.Add(new NodeViewModel("Type in another box here", 180, 150));
graphNodes.Add(new NodeViewModel("This is fun", 160, 70));
graphNodes.Add(new NodeViewModel("Why is this working", 320, 230));
graphNodes.Add(new NodeViewModel("ABC", 20, 170));
}
}
}
Created a working solution!
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace WpfApplication1
{
public class ResizeGizmo : Thumb
{
public ResizeGizmo()
{
DragDelta += new DragDeltaEventHandler(this.ResizeGizmo_DragDelta);
}
private void ResizeGizmo_DragDelta(object sender, DragDeltaEventArgs e)
{
float MinSize = 70;
NodeViewModel node = this.DataContext as NodeViewModel;
var element = sender as FrameworkElement;
if (node != null)
{
double deltaVertical, deltaHorizontal;
switch (VerticalAlignment)
{
case VerticalAlignment.Bottom:
deltaVertical = Math.Min(-e.VerticalChange, node.Height - MinSize);
node.Height -= deltaVertical;
break;
case VerticalAlignment.Top:
deltaVertical = Math.Min(e.VerticalChange, node.Height - MinSize);
node.Y += deltaVertical;
node.Height -= deltaVertical;
break;
default:
break;
}
switch (HorizontalAlignment)
{
case HorizontalAlignment.Left:
deltaHorizontal = Math.Min(e.HorizontalChange, node.Height - MinSize);
node.X += deltaHorizontal;
node.Width -= deltaHorizontal;
break;
case HorizontalAlignment.Right:
deltaHorizontal = Math.Min(-e.HorizontalChange, node.Height - MinSize);
node.Width -= deltaHorizontal;
break;
default:
break;
}
}
e.Handled = true;
}
}
}
Related
I would like to create an horizontal dynamic listbox:
A button is visible when the mouse is between two items.
<ListBox
MinHeight="32"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
SelectionMode="Extended"
HorizontalAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem>
<TextBlock><Run Text="C1" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C2" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C3" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C4" /></TextBlock>
</ListBoxItem>
</ListBox>
</ListBox>
Any suggestions, please?
Thank you
EDIT
private void myElement_MouseEnter(object sender, MouseEventArgs e)
{
//change button visibility
if (sender is Grid item)
{
if (item.DataContext is DataModel data)
{
System.Diagnostics.Debug.WriteLine("myElement_MouseEnter: " + data.TextValue);
}
var border1 = (Border)item.FindName("HitTestBorder1");
var border2 = (Border)item.FindName("HitTestBorder2");
if (border1 is Border)
{
var margin = border1.Margin;
margin.Left = -item.ActualWidth;
border1.Margin = margin;
}
if (border2 is Border)
{
var margin = border2.Margin;
margin.Left = item.ActualWidth;
border2.Margin = margin;
}
}
}
The problem is that the grid width is resized after mouse_enter... So, I don't get the overlay "effect".
Here is the solution.
I created a canvas. It contains 2 borders with button inside and ZPanel is setted very high (=1000) to be on top of everything.
I change the position of these borders on MouseEnter and MouseLeave events of ListBoxItem (see ItemTemplate).
XAML
<Border Padding="10">
<Canvas x:Name="supergrid" MouseLeave="supergrid_MouseLeave">
<Border x:Name="HitTestBorder1"
Panel.ZIndex="1000"
Background="Transparent"
BorderBrush="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderThickness="0,0,0,0" >
<Button x:Name="button1"
Visibility="Hidden"
Content="+"
Height="18" Width="18" FontSize="6" Background="Red"
Click="button1_Click"
/>
</Border>
<Border x:Name="HitTestBorder2"
Panel.ZIndex="1000"
Background="Transparent"
BorderBrush="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderThickness="1,1,1,1" >
<Button x:Name="button2"
Visibility="Hidden"
Content="+"
Height="18" Width="18" FontSize="6" Background="Green"
Click="button2_Click"
/>
</Border>
<ListBox x:Name="HorizontalListBox"
ItemsSource="{Binding DataModels}"
Margin="0,0,0,0"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Background="Yellow" MouseLeave="HorizontalListBox_MouseLeave">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<!--<Setter Property="MinWidth" Value="60" />
<Setter Property="MinHeight" Value="40" />-->
<Setter Property="Background" Value="Blue" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="myElement"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MouseEnter="myElement_MouseEnter"
MouseLeave="myElement_MouseLeave"
Background="White">
<TextBlock x:Name="myText"
Margin="10"
Text="{Binding TextValue}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Canvas>
</Border>
C#
public partial class MainWindow : Window
{
private DataModel currentDataModel;
public ObservableCollection<DataModel> DataModels { get; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.DataModels = new ObservableCollection<DataModel>();
this.DataModels.Add(new DataModel("Item1"));
this.DataModels.Add(new DataModel("Item2"));
this.DataModels.Add(new DataModel("SuperMegaHyperLong"));
this.DataModels.Add(new DataModel("Item3"));
this.DataModels.Add(new DataModel("Item4"));
this.DataModels.Add(new DataModel("123"));
this.DataModels.Add(new DataModel("Item5"));
}
private void myElement_MouseEnter(object sender, MouseEventArgs e)
{
updateOverlay((Grid)sender);
}
private void updateOverlay(Grid lbi)
{
if (lbi is Grid item) //the grid of listboxitem
{
if (item.DataContext is DataModel data)
{
Debug.WriteLine("myElement_MouseEnter: " + data.TextValue);
}
var myText = (TextBlock)item.FindName("myText");
if (myText is TextBlock)
{
Point relativePoint = myText.TransformToAncestor(supergrid)
.Transform(new Point(0, 0));
Debug.WriteLine("relativePoint: " + relativePoint.ToString());
//update left button position
double w = button1.ActualWidth;
double h = button1.ActualHeight;
double x = relativePoint.X - w / 2.0 - myText.Margin.Left;
double y = relativePoint.Y;
updateMargin(HitTestBorder1,
x,
x + w,
y + h,
y);
//update right button position
w = button2.ActualWidth;
h = button2.ActualHeight;
x = relativePoint.X - w / 2.0 - myText.Margin.Left;
x += item.ActualWidth;
y = relativePoint.Y;
updateMargin(HitTestBorder2,
x,
x + w,
y + h,
y);
//show the button
button1.Visibility = button2.Visibility = Visibility.Visible;
//the current item
if (myText.DataContext is DataModel dm)
{
this.currentDataModel = dm;
}
}
}
}
private void updateMargin(Border border, double left, double right, double bottom, double top)
{
//border = HitTestBorder2;
var margin = border.Margin;
margin.Left = left;
margin.Right = right;
margin.Top = top;
margin.Bottom = bottom;
border.Margin = margin;
Debug.WriteLine("updateMargin Left: " + left.ToString() + "Right: " + right.ToString());
}
private void myElement_MouseLeave(object sender, MouseEventArgs e)
{
//change button visibility
if (sender is Grid item)
{
if (item.DataContext is DataModel data)
{
System.Diagnostics.Debug.WriteLine("myElement_MouseLeave: " + data.TextValue);
}
//button1.Visibility = button2.Visibility = Visibility.Hidden;
}
}
private void HorizontalListBox_MouseLeave(object sender, MouseEventArgs e)
{
Debug.WriteLine("HorizontalListBox_MouseLeave");
//button1.Visibility = button2.Visibility = Visibility.Hidden;
}
private void supergrid_MouseLeave(object sender, MouseEventArgs e)
{
Debug.WriteLine("supergrid_MouseLeave: ");
button1.Visibility = button2.Visibility = Visibility.Hidden;
// => :D
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("LEFT INSERT");
int idx = this.DataModels.IndexOf(currentDataModel);
DataModel newDataModel = new DataModel($"Item{this.DataModels.Count}");
this.DataModels.Insert(idx, newDataModel);
button1.Visibility = button2.Visibility = Visibility.Hidden;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("RIGHT INSERT");
int idx = this.DataModels.IndexOf(currentDataModel);
this.DataModels.Insert(idx+1, new DataModel($"Item{this.DataModels.Count}"));
button1.Visibility = button2.Visibility = Visibility.Hidden;
}
}
public class DataModel
{
public string TextValue { get; set; }
public DataModel(string textValue)
{
this.TextValue = textValue;
}
}
As you can see, there is place for improvements (like select the new ListBoxItem). Disable the buttons when we drag, animation, delay, etc.
I am using a ScatterDataPoint chart in wpf,C#,.NET 3.5 and I have to add 3 horizontal lines for minimum, maximum and average value line that can be different depending on the values that I introduce.
Basically I take some values from a DB and I have to display them in a chart, below you have the basic code from which I started to create my application, but I do not know hoe to add those 3 lines.
here is the XAML code :
<Window.Resources>
<Style x:Key="BubbleDataPointStyle" TargetType="chartingToolkit:ScatterDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:ScatterDataPoint">
<Viewbox x:Name="viewbox">
<Ellipse Width="1px" Height="1px" Fill="Black"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="3"/>
<Setter Property="Height" Value="3"/>
</Style>
</Window.Resources>
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0,-28,0,28">
<Grid Height="921" Background="WhiteSmoke">
<chartingToolkit:Chart Name="lineChart" Title="Power Graph" Background="WhiteSmoke" Foreground="Black" VerticalAlignment="Top" Margin="16,36,20,0" Height="800" IsEnabled="True">
<chartingToolkit:ScatterSeries Title="Points" ItemsSource="{Binding}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" IsSelectionEnabled="True" DataPointStyle="{StaticResource BubbleDataPointStyle}">
<chartingToolkit:ScatterSeries.IndependentAxis>
<chartingToolkit:LinearAxis Orientation="X" Title="Time (Mins)" Interval="5" />
</chartingToolkit:ScatterSeries.IndependentAxis>
<chartingToolkit:ScatterSeries.DependentRangeAxis>
<chartingToolkit:LinearAxis Orientation="Y" Title="Lenght" x:Name="Yaxis"/>
</chartingToolkit:ScatterSeries.DependentRangeAxis>
</chartingToolkit:ScatterSeries>
</chartingToolkit:Chart>
</Grid>
</ScrollViewer>
</Grid>
</Window>
Here is the code behind, now it just generates a random point:
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();
ObservableCollection<KeyValuePair<double, double>> Power = new ObservableCollection<KeyValuePair<double, double>>();
public MainWindow()
`enter code here` {
InitializeComponent();
showColumnChart();
timer.Interval = new TimeSpan(0,0,0,0,1); // per 5 seconds, you could change it
timer.Tick += new EventHandler(timer_Tick);
timer.IsEnabled = true;
}
double i = 1;
Random random = new Random();
void timer_Tick(object sender, EventArgs e)
{
Power.Add(new KeyValuePair<double, double>(i, random.NextDouble()));
i += 1;
if(Power.Count==500)
{
timer.IsEnabled = false;
}
}
private void showColumnChart()
{
lineChart.DataContext = Power;
}
}
}
Normally you would use a StripLine for that, but the toolkit doesn't seem to have one. So use an extra LineSeries instead:
XAML:
<Window x:Class="WpfApplication336.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:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
xmlns:local="clr-namespace:WpfApplication336"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="BubbleDataPointStyle" TargetType="chartingToolkit:ScatterDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:ScatterDataPoint">
<Viewbox x:Name="viewbox">
<Ellipse Width="1px" Height="1px" Fill="Black"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="3"/>
<Setter Property="Height" Value="3"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<chartingToolkit:Chart Grid.Row="0" Name="lineChart" Title="Power Graph" Background="WhiteSmoke" Foreground="Black" IsEnabled="True">
<chartingToolkit:Chart.Series>
<chartingToolkit:ScatterSeries Title="Points" ItemsSource="{Binding Power}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" IsSelectionEnabled="True" DataPointStyle="{StaticResource BubbleDataPointStyle}"/>
<chartingToolkit:LineSeries Title="Average" ItemsSource="{Binding PowerAvg}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" />
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>
<Button Grid.Row="1" Click="Button_Click">START</Button>
</Grid>
ViewModel:
public class MyViewModel
{
public ObservableCollection<KeyValuePair<double, double>> Power { get; set; }
public ObservableCollection<KeyValuePair<double, double>> PowerAvg { get; set; }
public MyViewModel()
{
Power = new ObservableCollection<KeyValuePair<double, double>>();
PowerAvg = new ObservableCollection<KeyValuePair<double, double>>();
}
public void Add(double x, double y)
{
Power.Add(new KeyValuePair<double, double>(x, y));
double xmin = Power.Min(kvp => kvp.Key);
double xmax = Power.Max(kvp => kvp.Key);
double ymin = Power.Min(kvp => kvp.Value);
double ymax = Power.Max(kvp => kvp.Value);
double yavg = Power.Average(kvp => kvp.Value);
PowerAvg.Clear();
PowerAvg.Add(new KeyValuePair<double, double>(xmin, yavg));
PowerAvg.Add(new KeyValuePair<double, double>(xmax, yavg));
}
}
MainWindow:
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();
MyViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new MyViewModel();
DataContext = vm;
//showColumnChart();
timer.Interval = new TimeSpan(0, 0, 0, 0, 1); // per 5 seconds, you could change it
timer.Tick += new EventHandler(timer_Tick);
//timer.IsEnabled = true;
}
double i = 1;
Random random = new Random();
void timer_Tick(object sender, EventArgs e)
{
vm.Add(i, random.NextDouble());
i += 1;
if (vm.Power.Count == 250)
{
timer.Stop();
}
}
private void showColumnChart()
{
lineChart.DataContext = vm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
timer.Start();
}
}
You can easily add extra LineSeries for min and max just like we did for the average.
Expander button is not working properly in my wpf datagrid. I am using the following template for expander button.
<!-- MouseOver, Pressed behaviours-->
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Stroke"
Value="#FF3C7FB1"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#222"
TargetName="Sign"/>
</Trigger>
<Trigger Property="IsPressed"
Value="true">
<Setter Property="Stroke"
Value="#FF526C7B"
TargetName="Circle"/>
<Setter Property="StrokeThickness"
Value="1.5"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#FF003366"
TargetName="Sign"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Simple Expander Template-->
<ControlTemplate x:Key="SimpleExpanderTemp" TargetType="{x:Type Expander}">
<DockPanel>
<ToggleButton x:Name="ExpanderButton"
DockPanel.Dock="Top"
Template="{StaticResource SimpleExpanderButtonTemp}"
Content="{TemplateBinding Header}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
OverridesDefaultStyle="True"
Padding="1.5,0">
</ToggleButton>
<ContentPresenter x:Name="ExpanderContent"
Grid.Row="1"
Visibility="Collapsed"
DockPanel.Dock="Bottom"/>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="ExpanderContent" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Also i am adding the following code for datagridtemplatecolumn.
<DataGridTemplateColumn Width="27">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Expander Template="{StaticResource SimpleExpanderTemp}" Expanded="Expander_Expanded" Collapsed="Expander_Collapsed"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
But if i am trying to expand one expander button in my wpf datagrid, some of the other expander buttons are also expanding(not every time) and some of the expander buttons are collapsing.
What is the error in this xaml code?
The expander events are,
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
ContentControl cc = sender as ContentControl;
Expander exp = cc as Expander;
var itemsSource = objDatagrid.ItemsSource as IEnumerable;
if (itemsSource != null)
{
foreach (var item in itemsSource)
{
var row = objDatagrid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
if (row != null)
{
row.IsSelected = false;
}
}
}
for (var vis = sender as Visual; vis != null; vis = VisualTreeHelper.GetParent(vis) as Visual)
if (vis is DataGridRow)
{
var row = (DataGridRow)vis;
row.IsSelected = true;
if (exp.IsExpanded)
{
row.DetailsVisibility = Visibility.Visible;
exp.ExpandDirection = ExpandDirection.Down;
}
break;
}
}
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
var itemsSource = objDatagrid.ItemsSource as IEnumerable;
if (itemsSource != null)
{
foreach (var item in itemsSource)
{
var row = objDatagrid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
if (row != null)
{
row.IsSelected = false;
}
}
}
for (var vis = sender as Visual; vis != null; vis = VisualTreeHelper.GetParent(vis) as Visual)
if (vis is DataGridRow)
{
var row = (DataGridRow)vis;
row.IsSelected = true;
row.DetailsVisibility = row.DetailsVisibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
break;
}
}
You could try RowDetailsTemplate for the dataGrid. Also set the RowDetailsVisibilityMode="VisibleWhenSelected"
<DataGrid SelectionMode="Extended" ItemsSource="{Binding}" RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5">
<!-- Content -->
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.Columns>
<!-- Columns -->
</DataGrid.Columns>
</DataGrid>
</Grid>
I have next structure:
Main window
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid Margin="100 70 0 0">
<local:RotateControl x:Name="rtc1" Panel.ZIndex="1"
Width="{Binding ElementName=window, Path=ActualHeight, Converter={StaticResource SizeConverter}}"
Height="{Binding ElementName=window, Path=ActualHeight, Converter={StaticResource SizeConverter}}"
Radius="{Binding ElementName=window, Path=ActualHeight, Converter={StaticResource RadiusConverter}}"
Loaded="rtc1_Loaded" SizeChanged="rtc1_SizeChanged"/>
</Grid>
<StackPanel HorizontalAlignment="Right" Canvas.Right="80" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="year" Width="40"></TextBox>
<TextBox x:Name="month" Width="20"></TextBox>
<TextBox x:Name="day" Width="20"></TextBox>
<TextBox x:Name="hour" Width="20"></TextBox>
<TextBox x:Name="min" Width="20"></TextBox>
<TextBox x:Name="sec" Width="20"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox x:Name="lat" Width="80" Text="55.75"></TextBox>
<TextBox x:Name="lng" Width="80" Text="37.583333"></TextBox>
</StackPanel>
<ComboBox x:Name="cbHSys" Width="300" SelectedIndex="0"></ComboBox>
<Button x:Name="GatData" Click="GatData_Click">Get data</Button>
<ScrollViewer>
<Label x:Name="julday"></Label>
</ScrollViewer>
</StackPanel>
</Canvas>
Custom control
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Astro"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<local:XLineCoordConverter x:Key="XLineCoordConverter" />
<local:YLineCoordConverter x:Key="YLineCoordConverter" />
<SolidColorBrush x:Key="Blue1Brush" Color="#e2edfa" />
<SolidColorBrush x:Key="Blue2Brush" Color="#0080c0" />
<Pen x:Key="BlackPen1" Thickness="1" Brush="Black"></Pen>
<Pen x:Key="BluePen1" Thickness="0.1" Brush="{StaticResource Blue2Brush}"></Pen>
<Style TargetType="{x:Type local:RotateControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:RotateControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Background="Transparent">
<Line X1="{Binding Path=Radius}" Y1="{Binding Path=Radius}" Stroke="White" StrokeThickness="1">
<Line.X2>
<MultiBinding Converter="{StaticResource XLineCoordConverter}" ConverterParameter="1">
<Binding Path="Radius"/>
<Binding Path="House1"/>
<Binding Path="House2"/>
</MultiBinding>
</Line.X2>
<Line.Y2>
<MultiBinding Converter="{StaticResource YLineCoordConverter}" ConverterParameter="1">
<Binding Path="Radius"/>
<Binding Path="House1"/>
<Binding Path="House2"/>
</MultiBinding>
</Line.Y2>
</Line>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And model:
public class CircleModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
double _radius;
public double Radius
{
get
{
return _radius;
}
set
{
if (_radius == value) return;
_radius = value;
OnPropertyChanged("Radius");
}
}
double _angel;
public double Angel
{
get
{
return _angel;
}
set
{
if (_angel == value) return;
_angel = value;
OnPropertyChanged("Angel");
}
}
double _house1;
public double House1
{
get
{
return _house1;
}
set
{
if (_house1 == value) return;
_house1 = value;
OnPropertyChanged("House1");
}
}
...................
}
And code for change data:
private void GetData()
{
julday.Content = "";
//SetCurDate();
DateTime date = new DateTime(Convert.ToInt32(year.Text), Convert.ToInt32(month.Text), Convert.ToInt32(day.Text),
Convert.ToInt32(hour.Text), Convert.ToInt32(min.Text), Convert.ToInt32(sec.Text));
CalcNatalChart(date);
_model.Radius = (this.ActualHeight - (this.ActualHeight > 150 ? 150 : 0)) / 2;
this.ViewModel = _model;
}
private void GatData_Click(object sender, RoutedEventArgs e)
{
GetData();
}
private void rtc1_Loaded(object sender, RoutedEventArgs e)
{
GetData();
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(1.0);
_timer.Tick += timer_Tick;
_timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
julday.Content = "";
CalcNatalChart(DateTime.Now);
_model.Radius = (this.ActualHeight - (this.ActualHeight > 150 ? 150 : 0)) / 2;
this.ViewModel = _model;
}
All data in the form of updated correctly but View not. Only if press button GatData_Click. Dynamically change the data does not work.
Can you help me to solve it problem? Thanks
The problem is in these lines:
_model.Radius = (this.ActualHeight - (this.ActualHeight > 150 ? 150 : 0)) / 2;
this.ViewModel = _model;
After you reassign ViewModel, the view doesn't know about this change and uses old object instance. Either initialize ViewModel only once in constructor and then change it's properties or make that ViewModel property also observable so that you could notify when the data is changed.
Try this one...
instead of your this.ViewModel = _model; put this.ViewModel.Radious = _model.Radious;
i have four classes
public class BoolToCol : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool v = (bool)value;
if (v == true)
{
return new SolidColorBrush(Colors.Red);
}
else
{
return new SolidColorBrush(Colors.Blue);
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Cell : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool _ItsAlive;
public int _CellIndex;
public bool ItsAlive {
get {return _ItsAlive;}
set{
_ItsAlive = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ItsAlive"));
}
}
}
public int CellIndex
{
get { return _CellIndex; }
set
{
_CellIndex = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CellIndex"));
}
}
}
}
<Window x:Class="Pucketts_ConWaysGameOfLife.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Game of Life" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40*"/>
<ColumnDefinition Width="58*"/>
</Grid.ColumnDefinitions>
<Grid Background="Black"
Grid.Column="0">
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Width"
Value="80"/>
<Setter Property="Height"
Value="20"/>
<Setter Property="HorizontalAlignment"
Value="Left"/>
<Setter Property="VerticalAlignment"
Value="Top"/>
<Setter Property="BorderThickness"
Value="0"/>
<Setter Property="Foreground"
Value="Red"/>
<Setter Property="Background"
Value="Black"/>
<Setter Property="Grid.Row"
Value="3"/>
</Style>
<Style TargetType="Slider">
<Setter Property="TickFrequency"
Value="1"/>
<Setter Property="SmallChange"
Value="1"/>
<Setter Property="IsSnapToTickEnabled"
Value="True"/>
<Setter Property="LargeChange"
Value="10"/>
<Setter Property="Minimum"
Value="0"/>
<Setter Property="Width"
Value="80"/>
</Style>
<SolidColorBrush x:Key="foregroundBrush"
Color="Red"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="68"/>
<ColumnDefinition Width="76"/>
<ColumnDefinition Width="68"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Grid.Row="2"
Content="GenSlide"
Foreground="{StaticResource foregroundBrush}"/>
<Label Grid.Column="0"
Grid.Row="1"
Content="Width"
Foreground="{StaticResource foregroundBrush}"/>
<Label Grid.Column="0"
Grid.Row="0"
Content="Height"
Foreground="{StaticResource foregroundBrush}"/>
<Slider Grid.Column="1"
Grid.Row="2"
LargeChange="2"
x:Name="GenSlide"
Maximum="15"/>
<Slider Grid.Column="1"
Grid.Row="1"
x:Name="WidthSlide"
Maximum="100"/>
<Slider Grid.Column="1"
Grid.Row="0"
x:Name="HeightSlide"
Maximum="100"/>
<Button Grid.Column="2"
Click="SetGrid"
Content="Set Grid"/>
<Button Grid.Column="0"
Click="Random"
Content="Random"/>
<Label HorizontalAlignment="Left"
Grid.Column="2"
Grid.Row="2"
x:Name="GenNum"
Content="{Binding ElementName=GenSlide, Path= Value}"
Foreground="{StaticResource foregroundBrush}"/>
<Label HorizontalAlignment="Left"
Grid.Column="2"
Grid.Row="1"
x:Name="WidthNum"
Content="{Binding ElementName=WidthSlide, Path= Value}"
Foreground="{StaticResource foregroundBrush}"/>
<Label HorizontalAlignment="Left"
Grid.Column="2"
Grid.Row="0"
x:Name="HeightNum"
Content="{Binding ElementName=HeightSlide, Path= Value}"
Foreground="{StaticResource foregroundBrush}"/>
<Button Grid.Column="1"
Click="Play"
Content="Play"/>
<Button Grid.Column="1"
VerticalAlignment="Bottom"
Click="Next"
Margin="0,0,0,-10"
Content="Next"/>
</Grid>
<UniformGrid x:Name="Board"
Grid.Column="1">
</UniformGrid>
</Grid>
</Window>
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
Cell[] CellA;
int _NumOfColumns;
int CellIndex;
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
this.InitializeComponent();
}
public int NumOfColumns
{
get { return _NumOfColumns; }
set
{
_NumOfColumns = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("NumOfColumns"));
}
}
}
private void Initializeboard()
{
NumOfColumns = (int) (this.HeightSlide.Value);
CellA = new Cell[NumOfColumns * (int) (this.WidthSlide.Value)];
for (int i = 0; i < this.HeightSlide.Value * this.WidthSlide.Value; i++)
{
Rectangle rec = new Rectangle();
rec.Stroke = Brushes.Green;
rec.MouseLeftButtonDown += new MouseButtonEventHandler(rec_MouseLeftButtonDown);
this.Board.Children.Add(rec);
Cell c = new Cell();
c.CellIndex = i;
Binding newB = new Binding("ItsAlive");
newB.Source = c;
newB.Converter = new BoolToCol();
rec.SetBinding(Rectangle.FillProperty, newB);
CellA[i] = c;
}
}
public void rec_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
foreach (UIElement item in this.Board.Children)
{
if (item.IsMouseOver)
{
Cell c = (item as Rectangle).GetBindingExpression(Rectangle.FillProperty).ResolvedSource as Cell;
c.ItsAlive = !c.ItsAlive;
//c.CellIndex = this.Board.Children.IndexOf((Rectangle) sender);
CellIndex = c.CellIndex;
Console.WriteLine(CellIndex + " " + CellA[CellIndex].ItsAlive);
}
}
}
public List<Cell> CheckNeighbours(Cell c)
{
//NumOfColumns
//c.CellIndex
// 3 secations the ones above below and next to
List<Cell> NebourCell = new List<Cell> { };
NebourCell.Clear();
for (int i = -1; i < 2; i++)
{
try
{
NebourCell.Add(CellA[c.CellIndex - NumOfColumns - i]);
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Ignore");
}
}
for (int i = -1; i < 2; i += 2)
{
try
{
NebourCell.Add(CellA[c.CellIndex -i]);
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Ignore for middle index out of bounds");
}
}
for (int i = -1; i < 2; i++)
{
try
{
NebourCell.Add(CellA[c.CellIndex + NumOfColumns - i]);
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Ignore");
}
}
return NebourCell;
}
private void SetGrid(object sender, RoutedEventArgs e)
{
this.Board.Children.Clear();
this.Initializeboard();
}
private void Random(object sender, RoutedEventArgs e)
{
Random rand = new Random();
foreach (UIElement item in this.Board.Children)
{
Cell c = (item as Rectangle).GetBindingExpression(Rectangle.FillProperty).ResolvedSource as Cell;
if (rand.Next(0, 2) == 1)
{
c.ItsAlive = !c.ItsAlive;
}
}
}
private void Play(object sender, RoutedEventArgs e)
{
foreach (UIElement item in this.Board.Children)
{
}
}
private void Next(object sender, RoutedEventArgs e)
{
int Alive = 0;
foreach (Cell AllCell in CellA)
{
List<Cell> NebourCells = CheckNeighbours(CellA[AllCell.CellIndex]);
foreach ( Cell AllBebourCells in NebourCells)
{
Console.WriteLine(CellA[AllBebourCells.CellIndex].ItsAlive);
if (CellA[AllBebourCells.CellIndex].ItsAlive)
Alive++;
}
Console.WriteLine(Alive);
if (Alive < 2 || Alive > 3)
{
Console.WriteLine("It dies");
CellA[CellIndex].ItsAlive = false;
}
if (Alive == 2 || Alive == 3)
{
Console.WriteLine("Its lives on");
}
if (Alive == 3)
{
Console.WriteLine("Its alive");
CellA[CellIndex].ItsAlive = true;
}
//foreach (Cell item in NebourCell)
//{
// Rectangle ind = this.Board.Children[item.CellIndex] as Rectangle;
// ind.Fill = new SolidColorBrush(Colors.LightCyan);
//}
}
}
}
My problem i believe is in the Main Window cs and when you put three squares in a row and you go to next gen it is suppose to go vertical but mine just deletes the farthest one to the right i have been working on this for a while now i have been doing a lot of Debugging i have also debugged with break points i think it also is in the Next Button event Method but i could not find the problem and was wondering if anyone could assist me.
You have two problems with your Next method that I can see. First, you are not resetting your 'Alive' value between each cell that you process. So if the first cell you process has two alive neighbors, you Alive variable gets set to 2, and then if the next cell also has two alive neighbors, Alive is now set to 4!
Secondly, you are currently modifying the cells in place. This will lead to incorrect results, as if a certain cell dies, it will no longer be counted as alive for the processing of the cells next to it when it should be. You will need to create a temporary cell array that is a copy of CellA, and use the alive status of the cell in CellA but modify the status in the copy. Then at the end, set CellA to the copy.
Your new function should flow like this, in pseudocode
private void Next(object sender, RoutedEventArgs e)
{
Cell[] copy = copy of CellA
Loop over cells in copy
{
Alive = number of living neighbors of cell in CellA array
// apply Life logic here
copy[cell index].IsAlive = new status based on logic
}
CellA = copy
}
When you evolve your population (your CellA variable), you have to work on a copy of it. That is, you generate the new generation of cells into a newly allocated variable that you then assign to CellA only when you are done.