2nd Legend canvas not showing in wpf chart - c#

I've been struggling with this for some time now. The problem relates to adding a second legend canvas in a wpf chart. I'm referencing Jack Yu's book Practical WPF Charts and Graphics LineChartWithLegend.xaml file. In the xaml file, I added the new legend canvas named "legendCanvas2". I've changed the code behind to add a second instance of the legend in the AddChart() method. The problem is the second legend does not show inside chartCanvas. I suspect this issue has to do with multiple canvas containers inside chartCanvas but not sure. Any help with alternative ways I can display two legends inside chartCanvas would be appreciated.
XAML
<Window x:Class="LineCharts.LineChartWithLegend"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Line Chart with Legend" Height="400" Width="500">
<Grid Name="grid1" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Name="column1" Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Name="row1" Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Margin="2" x:Name="tbTitle" Grid.Column="1" Grid.Row="0"
RenderTransformOrigin="0.5,0.5" FontSize="14" FontWeight="Bold"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" TextAlignment="Center"
Text="Title"/>
<TextBlock Margin="2" x:Name="tbXLabel" Grid.Column="1" Grid.Row="2"
RenderTransformOrigin="0.5,0.5" TextAlignment="Center"
Text="X Axis"/>
<TextBlock Margin="2" Name="tbYLabel" Grid.Column="0" Grid.Row="1"
RenderTransformOrigin="0.5,0.5" TextAlignment="Center"
Text="Y Axis">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
<Grid Margin="0" x:Name ="chartGrid" Grid.Column="1" Grid.Row="1"
ClipToBounds="True" Background="Transparent" SizeChanged="chartGrid_SizeChanged" />
<Canvas Margin="2" Name="textCanvas" ClipToBounds="True" Grid.Column="1" Grid.Row="1">
<Canvas Name="chartCanvas" ClipToBounds="True">
<Canvas Name="legendCanvas" Background="Transparent" />
<Canvas Name="legendCanvas2" Background="Transparent" />
</Canvas>
</Canvas>
</Grid>
</Window>
Code-Behind
private void AddChart()
{
cs = new ChartStyleGridlines();
lg = new Legend();
lg2 = new Legend();
dc = new DataCollection();
ds = new DataSeries();
cs.ChartCanvas = chartCanvas;
cs.TextCanvas = textCanvas;
cs.Title = "Sine and Cosine Chart";
cs.Xmin = 0;
cs.Xmax = 7;
cs.Ymin = -1.5;
cs.Ymax = 1.5;
cs.YTick = 0.5;
cs.GridlinePattern = ChartStyleGridlines.GridlinePatternEnum.Dot;
cs.GridlineColor = Brushes.Black;
cs.AddChartStyle(tbTitle, tbXLabel, tbYLabel);
// Draw Sine curve:
ds.LineColor = Brushes.Blue;
ds.LineThickness = 1;
ds.SeriesName = "Sine";
for (int i = 0; i < 70; i++)
{
double x = i / 5.0;
double y = Math.Sin(x);
ds.LineSeries.Points.Add(new Point(x, y));
}
dc.DataList.Add(ds);
// Draw cosine curve:
ds = new DataSeries();
ds.LineColor = Brushes.Red;
ds.SeriesName = "Cosine";
ds.LinePattern = DataSeries.LinePatternEnum.DashDot;
ds.LineThickness = 2;
for (int i = 0; i < 70; i++)
{
double x = i / 5.0;
double y = Math.Cos(x);
ds.LineSeries.Points.Add(new Point(x, y));
}
dc.DataList.Add(ds);
// Draw sine^2 curve:
ds = new DataSeries();
ds.LineColor = Brushes.DarkGreen;
ds.SeriesName = "Sine^2";
ds.LinePattern = DataSeries.LinePatternEnum.Dot;
ds.LineThickness = 2;
for (int i = 0; i < 70; i++)
{
double x = i / 5.0;
double y = Math.Sin(x) * Math.Sin(x);
ds.LineSeries.Points.Add(new Point(x, y));
}
dc.DataList.Add(ds);
dc.AddLines(cs);
lg.LegendCanvas = legendCanvas;
lg.IsLegend = true;
lg.IsBorder = true;
lg.LegendPosition = Legend.LegendPositionEnum.NorthWest;
lg.AddLegend(cs.ChartCanvas, dc);
lg2 = new Legend();
lg2.LegendCanvas = legendCanvas2;
lg2.IsLegend = true;
lg2.IsBorder = true;
lg2.LegendPosition = Legend.LegendPositionEnum.NorthEast;
lg2.AddLegend(cs.ChartCanvas, dc);
}
private void chartGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
textCanvas.Width = chartGrid.ActualWidth;
textCanvas.Height = chartGrid.ActualHeight;
legendCanvas.Children.Clear();
legendCanvas2.Children.Clear();
chartCanvas.Children.RemoveRange(2, chartCanvas.Children.Count - 1); // changed index from 1 to 2
textCanvas.Children.RemoveRange(1, textCanvas.Children.Count - 1);
AddChart();
}

Related

UWP: Adjusting InkCanvas size when writing/drawing

I'm learning how to develop UWP apps and I'm using Microsoft's documentation as tutorials/research.
I want to have an InkCanvas design similar to OneNote where the InkCanvas height and width can expand (as you're writing/drawing and reach the end of the window size) and can shrink (when you erase ink strokes and the extra size can decrease based on the position of the ink strokes until you get back to the original size).
I'm able to increase the InkCanvas width and height, but can't decrease when erasing ink strokes.
Here is a MainPage.xaml code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Heading"
FontSize="36"
FontWeight="Bold"
Margin="10"
Grid.Column="0"
Grid.Row="0"/>
<Grid BorderBrush="Red"
BorderThickness="2"
Margin="10"
Grid.Column="0"
Grid.Row="1">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
HorizontalScrollMode="Enabled"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Enabled" >
<Grid BorderBrush="Blue"
BorderThickness="2"
Margin="1">
<InkCanvas Name="inkCanvas"/>
</Grid>
</ScrollViewer>
</Grid>
And the MainPage.cs code:
public MainPage()
{
this.InitializeComponent();
nkCanvas.InkPresenter.StrokeInput.StrokeEnded += adjustInkCanvasSize;
}
private async void adjustInkCanvasSize(InkStrokeInput sender, PointerEventArgs args)
{
await Task.Delay(100);
var XBound = inkCanvas.InkPresenter.StrokeContainer.BoundingRect.Bottom;
if (XBound > inkCanvas.ActualHeight - 200)
inkCanvas.Height = XBound + 200;
var YBound = inkCanvas.InkPresenter.StrokeContainer.BoundingRect.Right;
if (YBound > inkCanvas.ActualWidth - 200)
inkCanvas.Width = YBound + 200;
}
The c# code also came from another stackoverflow solution, but not able to figure out the "decrease" part.
Any help would be much appreciated. Thanks
If you want the InkCanvas control to shrink when you erase ink strokes and the extra size can decrease based on the position of the ink strokes until the original size, you need to add the InkPresenter.StrokesErased event to manage the size of the InkCanvas control. For example:
Here is a MainPage.xaml code( To facilitate testing, I added the mouse support):
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Heading"
FontSize="36"
FontWeight="Bold"
Margin="10"
Grid.Column="0"
Grid.Row="0"/>
<Grid BorderBrush="Red"
BorderThickness="2"
Margin="10"
Grid.Column="0"
Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<InkToolbar x:Name="inkToolbar" VerticalAlignment="Top" Margin="10,0,10,0"
TargetInkCanvas="{x:Bind inkCanvas}" Grid.Row="0"/>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
HorizontalScrollMode="Enabled" Grid.Row="1"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Enabled" >
<Grid BorderBrush="Blue"
BorderThickness="2"
Margin="1">
<InkCanvas Name="inkCanvas" />
</Grid>
</ScrollViewer>
</Grid>
</Grid>
And the MainPage.cs code:
public sealed partial class MainPage : Page
{
private double originalX; //The original size
private double originalY;
private double maxX=0.0;
private double maxY=0.0;
private bool flag = true;
public MainPage()
{
this.InitializeComponent();
inkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch |
CoreInputDeviceTypes.Pen;
inkCanvas.InkPresenter.StrokeInput.StrokeEnded += adjustInkCanvasSize;
inkCanvas.InkPresenter.StrokesErased += InkPresenter_StrokesErased;
}
private async void InkPresenter_StrokesErased(InkPresenter sender, InkStrokesErasedEventArgs args)
{
await Task.Delay(100);
//The coordinate of the lower right corner of the erased ink stoke
var erasedInkX= args.Strokes.ElementAt(0).BoundingRect.Bottom;
var erasedInkY = args.Strokes.ElementAt(0).BoundingRect.Right;
var XBound = inkCanvas.InkPresenter.StrokeContainer.BoundingRect.Bottom;
if (erasedInkX >=maxX&&XBound < inkCanvas.ActualHeight + 100)
{
if (XBound - 100 > originalX)
inkCanvas.Height = XBound - 100;
else
inkCanvas.Height = originalX; //The size of InkCanvas shrinks to the original size.
maxX = inkCanvas.Height;
}
var YBound = inkCanvas.InkPresenter.StrokeContainer.BoundingRect.Right;
if (erasedInkY>=maxY&&YBound < inkCanvas.ActualWidth + 100)
{
if (YBound - 100 > originalY)
{
inkCanvas.Width = YBound - 100;
}
else
inkCanvas.Width = originalY;
maxY = inkCanvas.Width;
}
}
private async void adjustInkCanvasSize(InkStrokeInput sender, PointerEventArgs args)
{
await Task.Delay(100);
if(flag)
{
flag = false;
originalX = inkCanvas.ActualHeight; //Get the original size
originalY = inkCanvas.ActualWidth;
}
var XBound = inkCanvas.InkPresenter.StrokeContainer.BoundingRect.Bottom;
if (XBound > maxX)
maxX = XBound; //maxX and maxY always hold the maximum size of StrokeContainer
if (XBound > inkCanvas.ActualHeight - 200)
inkCanvas.Height = XBound + 200;
var YBound = inkCanvas.InkPresenter.StrokeContainer.BoundingRect.Right;
if (YBound > maxY)
maxY = YBound;
if (YBound > inkCanvas.ActualWidth - 200)
inkCanvas.Width = YBound + 200;
}
}

EllipseGeometry Shrink & Fade Without Exceeding Canvas Bounds in C# WPF

How to make animation stay within the canvas at bigger sizes when the user clicks around the edge of the canvas? Currently, if sizes are too big and if user clicks near the edge of the canvas, the ellipse will grow outside of the canvas to cover the buttons. I need the animation to stay within the canvas to make it look like a slice of pizza essentially.
Should look like this:
Size 50 where user clicks near top left of canvas
Currently looks like this:
Size 50 where user clicks near top left of canvas
Xaml:
<Window.Resources>
<Storyboard x:Key="anim">
<DoubleAnimation
Storyboard.TargetName="myCircle"
Storyboard.TargetProperty="RadiusX"
AutoReverse="True"/>
<DoubleAnimation
Storyboard.TargetName="myCircle"
Storyboard.TargetProperty="RadiusY"
AutoReverse="True"/>
<DoubleAnimation
Storyboard.TargetName="path"
Storyboard.TargetProperty="Opacity"
AutoReverse="True"/>
</Storyboard>
</Window.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="23"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" Margin="0,0,0,1">
<Menu DockPanel.Dock="Top" Height="23">
<MenuItem Header="Main" RenderTransformOrigin="-1.896,0.643" HorizontalAlignment="Left" Width="39" Height="23">
<MenuItem Header="Exit, Esc" Click="MenuItem_Click_Exit"/>
</MenuItem>
</Menu>
</DockPanel>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" Name="pane">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Name="pane2">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Label Content="Size" Grid.Row="0" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
<Label Content="Fill Color" Grid.Row="1" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
<Label Content="Stroke Thickness" Grid.Row="2" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
<Label Content="Stroke Color" Grid.Row="3" Grid.Column="0" VerticalAlignment="Top" Height="25"/>
<Slider x:Name="Slider_Size" Grid.Row="0" Grid.Column="1" Height="20" Width="45"
Minimum="5" Maximum="50"
AutoToolTipPlacement="BottomRight"
TickFrequency="1"
IsSnapToTickEnabled="True"
PreviewMouseUp="Slider_Size_PreviewMouseUp"/>
<Label Name="tempSize" Content="{Binding Path=Value, ElementName=Slider_Size}" Margin="0,25,0,131" Grid.Row="3" Visibility="Hidden"/>
<ComboBox Name="ComboBox_FillColor" Grid.Row="1" Grid.Column="1" Height="20" Width="45" SelectionChanged="ComboBox_FillColor_Selected"/>
<TextBox Name="textBox" Grid.Row="2" Grid.Column="1" Height="20" Width="45" TextChanged="textBox_TextChanged"/>
<ComboBox Name="ComboBox_StrokeColor" Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Height="20" Width="45" SelectionChanged="ComboBox_StrokeColor_Selected"/>
</Grid>
<Border Name ="border" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderBrush="Black" Grid.Column="1" BorderThickness="2">
<Canvas Name="canvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseDown="Canvas_MouseDown">
<Path x:Name="path">
<Path.Data>
<EllipseGeometry x:Name="myCircle"/>
</Path.Data>
</Path>
<Canvas.Background>
<SolidColorBrush Color="White" Opacity="0"/>
</Canvas.Background>
</Canvas>
</Border>
</Grid>
</Grid>
C#:
public partial class MainWindow : Window
{
private int size;
private SolidColorBrush fillColor;
private SolidColorBrush strokeColor;
private List<SolidColorBrush> colors;
private int fillIndex;
private int strokeIndex;
private int strokeThickness = 1;
private int fillColorDefault;
private int strokeColorDefault;
private Point? _start = null;
public MainWindow()
{
InitializeComponent();
addColors();
textBox.Text = strokeThickness.ToString();
parse();
}
private void MenuItem_Click_Exit(object sender, RoutedEventArgs e) { Environment.Exit(1); }
private void Window_KeyUp_ESC(object sender, KeyEventArgs e)
{
if (Key.Escape == e.Key)
MenuItem_Click_Exit(sender, e);
}
private void addColors()
{
colors = typeof(Brushes).GetProperties().Select(p => p.GetValue(null, null) as SolidColorBrush).ToList();
int count = 0;
foreach (SolidColorBrush color in colors)
{
ComboBox_FillColor.Items.Add(new Rectangle() { Height = 12, Width = 17.5, Fill = color });
ComboBox_StrokeColor.Items.Add(new Rectangle() { Height = 12, Width = 17.5, Fill = color });
if (color.Color == Colors.Red)
{
fillIndex = count;
fillColor = colors[fillIndex];
ComboBox_FillColor.SelectedIndex = count;
fillColorDefault = count;
}
if (color.Color == Colors.Black)
{
strokeIndex = count;
strokeColor = colors[strokeIndex];
ComboBox_StrokeColor.SelectedIndex = count;
strokeColorDefault = count;
}
count++;
}
}
private void ComboBox_FillColor_Selected(object sender, RoutedEventArgs e) { fillIndex = ComboBox_FillColor.SelectedIndex; fillColor = colors[fillIndex]; }
private void ComboBox_StrokeColor_Selected(object sender, RoutedEventArgs e) { strokeIndex = ComboBox_StrokeColor.SelectedIndex; strokeColor = colors[strokeIndex]; }
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
path.Stroke = strokeColor;
path.StrokeThickness = strokeThickness;
path.Fill = fillColor;
path.HorizontalAlignment = HorizontalAlignment.Stretch;
path.VerticalAlignment = VerticalAlignment.Stretch;
path.Stretch = Stretch.None;
path.SetValue(Grid.ColumnProperty, 1);
_start = Mouse.GetPosition((UIElement)sender);
myCircle.Center = (Point)_start;
var sb = FindResource("anim") as Storyboard;
var x = sb.Children.First() as DoubleAnimation;
x.To = 2 * size;
x.Duration = new Duration(TimeSpan.FromSeconds(0.5));
var y = sb.Children.ElementAt(1) as DoubleAnimation;
y.To = 2 * size;
y.Duration = new Duration(TimeSpan.FromSeconds(0.5));
var z = sb.Children.Last() as DoubleAnimation;
z.From = 0.0;
z.To = 1.0;
z.Duration = new Duration(TimeSpan.FromSeconds(0.5));
sb.Begin(path);
}
private void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
//regex where any string of chars besides numbers
Regex pattern = new Regex(#"^([^0-9]*)$", RegexOptions.Compiled);
Match result = pattern.Match(textBox.Text);
if (textBox.Text.ToString() == string.Empty)
return;
else if (result.Success)
{
MessageBox.Show("Invalid character entered. Integer numbers only. Stroke Thickness will be reseted to a default of 1.");
strokeThickness = 1;
textBox.Text = strokeThickness.ToString();
textBox.SelectAll();
}
else
{
int x;
if (int.TryParse(textBox.Text, out x))
strokeThickness = int.Parse(textBox.Text);
}
}
private void Slider_Size_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
parse();
}
private void parse()
{
int x;
if (int.TryParse(tempSize.Content.ToString(), out x))
size = x;
}
}
}
So you don't need the ellipse to stay within the Canvas, but you want to clip away the parts leaving it, right? Just set ClipToBounds (of Canvas) to true (can be done in Xaml).

Grid or border not allowing drop

I have to do a drag and drop application listView to code-generated grid.
So I have done a test program and that works
here is the xaml
<Grid Margin="0,0,-61.6,0.4">
<ListView x:Name="lwOne" PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown" Background="Bisque" Margin="16,65,340.2,22">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Width="250" VerticalAlignment="Top"></WrapPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<Grid Name="grdMain" Drop="Grid_Drop" Background="AliceBlue" AllowDrop="True" Margin="380,65,81.2,62"/>
Now I have to migrate all this to my real application Which looks like that
So from how the cursor looks like i can see that the drag and drop is not allowed on the grid but it is on the tiny border of each cell.
So the problem is not doing d&d but ALLOWING to it
here is the xaml. The source listView is lvAllowedPPtab2, the destination grid is grdPalletTab2
<TabItem Name="tabItem2" HorizontalAlignment="Center" Height="80" MouseLeftButtonUp="TabItem_MouseLeftButtonUp" FontSize="{StaticResource TOOLTIP_FONTSIZE}" IsSelected="false" >
<TabItem.Header>
<StackPanel>
<TextBlock Text="" FontSize="{StaticResource TAB_FONTSIZE}"/>
<TextBlock Name="tbTab2" Visibility="Hidden" FontSize="{StaticResource BUTTON_FONTSIZE}" />
</StackPanel>
</TabItem.Header>
<TabItem.Background>
<ImageBrush/>
</TabItem.Background>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border x:Name="Border1Tab2" BorderBrush="Gainsboro" BorderThickness="5" Width="200" Margin="10,10,10,10" >
<StackPanel Margin="-1.8,-0.8,2.2,1.4">
<ListBox x:Name="lbxPalletsTab2" Background="{x:Null}" BorderBrush="{x:Null}" Height="600" SelectionChanged="ListBox_SelectionChanged" Margin="12,10.2,8.4,10.4" />
</StackPanel>
</Border>
<Border x:Name="Border2Tab2" BorderBrush="Gainsboro" MinWidth="150" BorderThickness="5" Grid.Column="1" Margin="10,10,10,10">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300px"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid HorizontalAlignment="Stretch" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="50px"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="tbkPPtab2" Grid.Row="0" FontSize="22" Background="{x:Null}" FontWeight="Black" Text="---" HorizontalAlignment="Center" VerticalAlignment="Bottom"></TextBlock>
<ListView x:Name="lvAllowedPPtab2" Grid.Row="1" FontSize="12" Background="{x:Null}" BorderBrush="Gainsboro" BorderThickness="5" Margin="10" VerticalAlignment="Stretch" PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown">
<ListView.ItemsPanel >
<ItemsPanelTemplate >
<WrapPanel Orientation="Horizontal" Width="250" Background="{x:Null}" VerticalAlignment="Top"></WrapPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
<Border Grid.Column="1" BorderBrush="Gainsboro" BorderThickness="5" Margin="10,60,10,10">
<Grid Name="grdPalletTab2" AllowDrop="True" Drop="Grid_Drop"/>
</Border>
</Grid>
</Border>
</Grid>
</TabItem>
the grid is formed through
PalletWindow.PalletWindow.SetPalletGrid(numRows, numColumns,ref grdPalletTab2);
whose code is:
public static bool SetPalletGrid(int numRows, int numColumns, ref Grid grd)
{
try
{
grd.Children.Clear();
grd.RowDefinitions.Clear();
grd.ColumnDefinitions.Clear();
grd.AllowDrop = true;
for (int row = 0; row < numRows; row++)
{
var rd = new RowDefinition();
rd.AllowDrop = true;
rd.Height = new GridLength(1.0, GridUnitType.Star);
grd.RowDefinitions.Add(rd);
}
for (int column = 0; column < numColumns; column++)
{
var cd = new ColumnDefinition();
cd.AllowDrop = true;
cd.Width = new GridLength(1.0, GridUnitType.Star);
grd.ColumnDefinitions.Add(cd);
}
for (int row = 0; row < numRows; row++)
{
for (int column = 0; column < numColumns; column++)
{
var borderImage = new Border();
borderImage.AllowDrop = true;
borderImage.BorderThickness = new Thickness(2);
borderImage.BorderBrush = new SolidColorBrush(Colors.Black);
borderImage.Name = "BRD_" + row + "_" + column;
borderImage.Effect = new DropShadowEffect
{
Color = new Color { R = 255, G = 255, B = 255 },
Direction = 320,
ShadowDepth = 5,
Opacity = 0.95
};
Grid.SetRow(borderImage, row);
Grid.SetColumn(borderImage, column);
grd.Children.Add(borderImage);
}
}
return true;
}
catch// (Exception exc)
{
return false;
}
}
thanks for any help
Patrick
To make d&d work you have to set the background of the target element (don't know why). In your case set borderImage = new SolidColorBrush(Colors.Transparent);

Not sure which property should I use for animation

I am creating a kiosk app in Windows 8 but it will be used as assigned access app in 8.1. I want to create an animation for ads. The idea of animation is attached as image with this thread. Basically there will be 6-10 images in a L shape (Right side a column & bottom side a row). Now one ad in extreme bottom right corner will be stationary. The ads in column will travers like HTML's marquee and reaches to row at that time the ads in row will travers and reaches to column. In this way the ads will keep of moving in a clock wise pattern. How can I achieve this in my C#/XAML app?
Please not the ads will be never be displayed on top or left. The ad is <Image /> & source is Internet URLs. All ads are in ItemsControl.
Yuck, let's start with how much I hate this app already.
Now to the answer.
I don't think you really need an animation. You will likely change the ads only every so often. And a simple change in their position and NOT a transition in their position seems adequate.
Try this:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Rectangle Fill="White" Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="0" Grid.RowSpan="3" />
<Rectangle x:Name="Ad1" Fill="Green" Grid.Column="3" Grid.Row="3" />
<Rectangle x:Name="Ad2" Fill="IndianRed" Grid.Column="0" Grid.Row="3" />
<Rectangle x:Name="Ad3" Fill="Red" Grid.Column="1" Grid.Row="3" />
<Rectangle x:Name="Ad4" Fill="DarkRed" Grid.Column="2" Grid.Row="3" />
<Rectangle x:Name="Ad5" Fill="Pink" Grid.Column="3" Grid.Row="0" />
<Rectangle x:Name="Ad6" Fill="HotPink" Grid.Column="3" Grid.Row="1" />
<Rectangle x:Name="Ad7" Fill="Purple" Grid.Column="3" Grid.Row="2" />
</Grid>
With this:
public MainPage()
{
this.InitializeComponent();
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
timer.Tick += (s, e) => Move();
timer.Start();
}
void Move()
{
var ads = new Rectangle[] { Ad1, Ad2, Ad3, Ad4, Ad5, Ad6, Ad7 };
foreach (var item in ads)
{
var row = (int)item.GetValue(Grid.RowProperty);
var col = (int)item.GetValue(Grid.ColumnProperty);
if (row == 3)
{
if (col == 0)
{
row = 0;
col = 3;
}
else
col--;
}
else
{
if (row == 2)
{
row = 3;
col = 2;
}
else
row++;
}
item.SetValue(Grid.RowProperty, row);
item.SetValue(Grid.ColumnProperty, col);
}
}
And it looks pretty good to me.
But if you must have animations, try this.
void Move()
{
var ads = new Rectangle[] { Ad1, Ad2, Ad3, Ad4, Ad5, Ad6, Ad7 };
foreach (var item in ads)
{
var row = (int)item.GetValue(Grid.RowProperty);
var col = (int)item.GetValue(Grid.ColumnProperty);
var x = item.ActualWidth;
var y = item.ActualHeight;
// bottom
if (row == 3)
{
// left-last
if (col == 0)
{
row = 0;
col = 3;
x = -x;
y = 0;
}
// others
else
{
col--;
x = -x;
y = 0;
}
}
// right
else
{
// bottom-last
if (row == 2)
{
row = 3;
col = 2;
x = -x;
}
else
{
row++;
x = 0;
}
}
var dr = new Duration(TimeSpan.FromSeconds(.5));
var tx = item.RenderTransform = new TranslateTransform();
var ax = new DoubleAnimation { To = x, Duration = dr };
Storyboard.SetTarget(ax, tx);
Storyboard.SetTargetProperty(ax, "X");
var ay = new DoubleAnimation { To = y, Duration = dr };
Storyboard.SetTarget(ay, tx);
Storyboard.SetTargetProperty(ay, "Y");
var st = new Storyboard { FillBehavior = FillBehavior.HoldEnd };
st.Children.Add(ax);
st.Children.Add(ay);
st.Completed += (s, e) =>
{
item.SetValue(Grid.RowProperty, row);
item.SetValue(Grid.ColumnProperty, col);
st.Stop();
};
st.Begin();
}
}
Best of luck!

UserControl inside a FlipView

Summary:
I cannot get a UserControl, which is inside a FlipView, to refresh/update itself when changing pages on the FlipView. The UserControl depends upon its code-behind to perform essential calculations for its own display.
Introduction:
I have created a graphing UserControl in Windows 8 due to the lack of such a control at the moment (apart from some 3rd parties). The XAML for the control creates the axes and declares the path used for actually drawing the graph-line, and the code-behind instantiates a class which generates the plot-points for this path. The instantiation of this graph-path class is made by the loaded event-handler in the code-behind once the axes have been properly rendered in order to get the size of the plotting area. The parameters to the constructor include various data specific to the plot-points plus references to the child control elements which are used for scaling.
Here's the control:
<Grid>
<Border Background="LightGray"
Margin="0,10">
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="40*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="1"
Grid.Column="0"
Grid.RowSpan="11"
Name="yAxisBorder">
<!--this canvas is written to in DrawGraph.cs -->
<Canvas Name="yAxis">
</Canvas>
</Border>
<Border Grid.Row="11"
Grid.Column="1"
Name="xAxisBorder"
BorderBrush="Black">
<!--this canvas is written to in DrawGraph.cs -->
<Canvas Name="xAxis"/>
</Border>
<Border x:Name ="GraphAxis"
Grid.Row="1"
Grid.Column="1"
Grid.RowSpan="10"
BorderThickness="2,2,2,2"
BorderBrush="Black"
Loaded="GraphAxis_Loaded">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop x:Name="GraphStop2"
Offset="0.554" Color="White"/>
</LinearGradientBrush>
</Border.Background>
<!--this path is written to in DrawGraph.cs -->
<Path x:Name="GraphLine"
StrokeThickness="2"
Data="M0,0">
<Path.Stroke>
<SolidColorBrush Color="Black"/>
</Path.Stroke>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="LightPink"
Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
</Border>
<!--These borders draw the grid-lines on the graph-->
<Border Grid.Row="1"
Grid.Column="1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black"/>
<Border Grid.Row="2"
Grid.Column="1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black">
</Border>
<Border Grid.Row="3"
Grid.Column="1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black">
</Border>
<Border Grid.Row="4"
Grid.Column="1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black">
</Border>
<!--This border is for drawing the x-axis for -ve inflation-->
<Border Grid.Row="5"
Grid.Column="1"
x:Name="xAxisLine1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black">
</Border>
<Border Grid.Row="6"
Grid.Column="1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black">
</Border>
<Border Grid.Row="7"
Grid.Column="1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black">
</Border>
<Border Grid.Row="8"
Grid.Column="1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black">
</Border>
<Border Grid.Row="9"
Grid.Column="1"
BorderThickness="0,0,0,0.5"
BorderBrush="Black">
</Border>
<Border x:Name="xAxisLine2"
Grid.Row="10"
Grid.Column="1"
BorderThickness="0,0,0,2"
BorderBrush="Black">
</Border>
</Grid>
</Border>
</Grid>
Here's the code-behind with the instantiation of the object which draws the graph line (the Path called GraphLine in the XAML:
public void GraphAxis_Loaded(object sender, RoutedEventArgs e)
{
ApplicationDataContainer appData = ApplicationData.Current.RoamingSettings;
Country selectedCountry = CountryDataSource.GetCountry((int)appData.Values["Country"]);
string month1 = selectedCountry.FirstMonth, month2 = selectedCountry.LastMonth;
int year1 = selectedCountry.FirstYear, year2 = selectedCountry.LastYear;
DrawGraph dGraph = new DrawGraph(selectedCountry, month1, year1, month2, year2,
GraphAxis.ActualHeight, GraphAxis.ActualWidth, GraphLine, xAxis, xAxisBorder, yAxis, MainGrid, xAxisLine1, xAxisLine2);
}
}
...and finally here's the DrawGraph class:
public class DrawGraph
{
List<String> months = new List<string> { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
public DrawGraph(Country currentC, string nearMonth, int nearYear, string farMonth, int farYear, double cHeight, double cWidth, Path pGraphLine, Canvas xAxis, Border xAxisBorder, Canvas yAxis, Grid GraphGrid, Border xAxisLine1, Border xAxisLine2)
{
int periods = 0, years = farYear - nearYear, firstDateIndex = 0, lastDateIndex = 0, n = 0, prIndex = 0, yScalePve = 0, yScaleNve = 0, yScaleExtent = 0;
double endDateIndex, minFactor = 1000, maxFactor = 0, inflationFactor,
baseFactor = 0, yOffSet = 0;
plotPoint[] plotResults;
cHeight -= 4; // this is for the 4 pixels taken up by the border
// count up the months between the start and end points
foreach (KeyValuePair<string, double> rpiRecord in currentC.RpiData)
{
if (rpiRecord.Key == nearMonth + nearYear.ToString())
{
firstDateIndex = n;
}
if (rpiRecord.Key == farMonth + farYear.ToString())
{
lastDateIndex = n;
}
n++;
}
// calculate the number of points to plot
periods = lastDateIndex - firstDateIndex + 1;
// put together the array of dates (relative) and factors to plot
if (periods > 1)
{
plotResults = new plotPoint[periods];
n = 0;
foreach (KeyValuePair<string, double> rpiRecord in currentC.RpiData)
{
if (n >= firstDateIndex & n <= lastDateIndex)
{
if (n == firstDateIndex)
{
baseFactor = rpiRecord.Value;
plotResults[prIndex] = new plotPoint(prIndex, 0);
minFactor = 0;
maxFactor = 0;
}
else
{
// get the inflation factor and date position and populate the plotPoints array
inflationFactor = (rpiRecord.Value - baseFactor) / baseFactor * 100;
plotResults[prIndex] = new plotPoint(prIndex, inflationFactor);
// these are used for the height scaling
if (inflationFactor > maxFactor)
{
maxFactor = inflationFactor;
}
if (inflationFactor < minFactor & rpiRecord.Value != 0)
{
minFactor = inflationFactor;
}
}
prIndex++;
}
n++;
}
// now decide on the y-axis scale
// firstly, is the maximum in the units, tens, hundreds or thousands
if (maxFactor - 1 < 0)
{
yScalePve = 1;
}
else if (maxFactor - 2 < 0)
{
yScalePve = 2;
}
else if (maxFactor - 5 < 0)
{
yScalePve = 5;
}
else if (maxFactor - 10 < 0)
{
yScalePve = 10;
}
else if (maxFactor - 50 < 0)
{
yScalePve = 50;
}
else if (maxFactor - 100 < 0)
{
yScalePve = 100;
}
else if (maxFactor - 500 < 0)
{
yScalePve = 500;
}
else if (maxFactor - 1000 < 0)
{
yScalePve = 1000;
}
else if (maxFactor - 5000 < 0)
{
yScalePve = 5000;
}
else
{
yScalePve = 10000;
}
if (minFactor < 0)
{
if (minFactor + 1 > 0)
{
yScaleNve = 1;
}
else if (minFactor + 2 > 0)
{
yScaleNve = 2;
}
else if (minFactor + 5 > 0)
{
yScaleNve = 5;
}
else if (minFactor + 10 > 0)
{
yScaleNve = 10;
}
else if (minFactor + 50 > 0)
{
yScaleNve = 50;
}
else if (minFactor + 100 > 0)
{
yScaleNve = 100;
}
else if (minFactor + 500 > 0)
{
yScaleNve = 500;
}
else if (minFactor + 1000 > 0)
{
yScaleNve = 1000;
}
else if (minFactor + 5000 > 0)
{
yScaleNve = 5000;
}
else
{
yScaleNve = 10000;
}
// calculate the position of the xAxis on the yScale
if (maxFactor <= minFactor * -100) // this is to prevent small -ves moving the x-axis to the middle
{
yOffSet = cHeight / 2;
}
}
// calculate the position of the xAxis on the yScale
yScaleExtent = yScalePve + yScaleNve;
if (cHeight > 0) // this prevents the borders being re-sized before the grids have been rendered
{
// this re-sizes the rows in the grid displaying the graph - there is a border in the 5th row (defined in the XAML)
// which acts as the x axis and this adjusts the line thickness to where the x axis should be
if (yOffSet == 0)
{
// this draws the x-axis when it is at the bottom position
xAxisLine1.BorderThickness = new Thickness(0, 0, 0, 1);
xAxisLine2.BorderThickness = new Thickness(0, 0, 0, 2);
cHeight += 1; // this is for the extra pixel taken up by the border
}
else
{
// this draws the x-axis when it is at the middle position
xAxisLine1.BorderThickness = new Thickness(0, 0, 0, 2);
xAxisLine2.BorderThickness = new Thickness(0, 0, 0, 1);
cHeight -= 1; // this is for the extra pixel taken up by the border
}
}
// this is used for the width scaling
endDateIndex = plotResults[plotResults.Length - 1].plotDate;
// zero-base the plot results
plotResults[0].plotDate = 0;
plotResults[0].plotFactor = 0 + yOffSet;
PathFigure myPathFigure = new PathFigure();
myPathFigure.StartPoint = new Point(0, cHeight - yOffSet);
PathSegmentCollection myPathSegmentCollection = new PathSegmentCollection();
for (int x = 1; x < plotResults.Length; x++)
{
// re-base the array dates with respect to the plot area width
plotResults[x].plotDate *= cWidth / endDateIndex;
// re-base the array factors with respect to the y-axis
plotResults[x].plotFactor = plotResults[x].plotFactor / yScaleExtent;
// then re-base the array into the units of the plot area
plotResults[x].plotFactor = plotResults[x].plotFactor * (cHeight - yOffSet);
// generate the LineSegment objects for each plotPoint
LineSegment myLineSegment = new LineSegment();
myLineSegment.Point = new Point(plotResults[x].plotDate, (cHeight - yOffSet) - plotResults[x].plotFactor);
myPathSegmentCollection.Add(myLineSegment);
}
// these 2 additional line segments create an enclosed polygon for the fill to go into
LineSegment myLineSegment1 = new LineSegment();
// this line draws down vertically on the right-hand y axis
myLineSegment1.Point = new Point(cWidth, cHeight - yOffSet); //
myPathSegmentCollection.Add(myLineSegment1);
LineSegment myLineSegment2 = new LineSegment();
// this line draws across to the left on the x axis
myLineSegment2.Point = new Point(0, cHeight - yOffSet);
myPathSegmentCollection.Add(myLineSegment2);
// this sets up the LineSegments for the PathFigure, the
// PathFigure for the PathGeometry and the PathGeometry for the PathData!!
myPathFigure.Segments = myPathSegmentCollection;
PathFigureCollection myPathFigureCollection = new PathFigureCollection();
myPathFigureCollection.Add(myPathFigure);
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures = myPathFigureCollection;
pGraphLine.Data = myPathGeometry;
Calculate_xAxis(nearYear, farYear, nearMonth, farMonth, xAxis, xAxisBorder);
Calculate_yAxis(yScalePve, yScaleNve, xAxis, yAxis, yOffSet);
}
}
Issue:
I want to display this graphing control within a FlipView which is bound to an observable collection which holds sets of data used by both the FlipView and the path-drawing class. As you can see in the code-behind, there is no data binding in the graph control itself. The data is obtained from the ApplicationData and control element sizes within the UserControl itself. This all works fine when the FlipView starts up but when I flip the pages the graphs do not update.
I have looked at this problem from a couple of points-of-view:
Firstly, it appears to be the case that when flipping the pages in the FlipView, the loaded event does not re-fire on the axis control (a Border) within the UserControl within the FlipView, but it does fire first time around. I cannot find any other event to hook into other than the loaded event.
Secondly, would it be possible to make the entire graph work off data-binding? I understand that I'd have to create Dependency Properties in the UserControl to do this but even then, I would still need to instantiate a DrawGraph in code-behind somehow.
Can anyone here see if there is a solution to this problem? Or am I approaching this incorrectly? Should I be putting all of the DrawGraph functionality into a routine somewhere else which pre-populates the Observable Collection with all of the plot-points and then bind the graph UserControl to this?
Any suggestions most welcome. I'm fairly new to this and this is my first posting here and am hopeful that someone can educate me in my approach. Thank you

Categories

Resources