I am trying what I thought a simple thing: drawing lines of a list of point.
If I put the list statically in the xaml of my window everything is ok.
If I do the bind, then nothing is displayed.
the window code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Polyline Stretch="Fill" Grid.Column="0" Name="polyline" Stroke="Red" DataContext="{Binding Points}">
</Polyline>
</Grid>
public partial class testWindow2 : Window
{
AudioSignalModelView audioSignalModelView;
public testWindow2()
{
InitializeComponent();
audioSignalModelView = new AudioSignalModelView();
this.DataContext = audioSignalModelView;
}
}
public class AudioSignalModelView
{
public AudioSignalModelView()
{
Point pointA = new Point {X=0,Y=0};
Point pointB = new Point { X = 0.2, Y = 0.4 };
Point pointC = new Point { X = 0.8, Y = 0.1 };
Point pointD = new Point { X = 1, Y = 1 };
Points.Add(pointA);
Points.Add(pointB);
Points.Add(pointC);
Points.Add(pointD);
}
private AudioSignalTest audioSignalTest;
private PointCollection _points = new PointCollection();
public PointCollection Points
{
get { return _points; }
}
}
I think the binding is done somehow, because if I put a breakpoint in the getter of the Points property, it is called by the system...
What is obviously wrong in my code ?
You want to bind to the Points property not the DataContext.
<Polyline Stretch="Fill" Grid.Column="0"
Name="polyline" Stroke="Red"
Points="{Binding Points}"> <-- Here
</Polyline>
MSDN page
Related
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();
}
So I'm trying to make 8 circles which all have different fill colours and all have different blinking speeds. So far I have been able to make them blink at different speeds, but I'm having trouble with making them all have different colours. My code so far:
private void Appear(Ellipse element, double duration, Brush colour)
{
element.Fill = colour;
DoubleAnimation db = new DoubleAnimation();
db.From = 0.0;
db.To = 1.0;
db.Duration = new Duration(TimeSpan.FromSeconds(duration));
db.RepeatBehavior = RepeatBehavior.Forever;
element.BeginAnimation(Ellipse.OpacityProperty, db);
}
private Brush SetEllipseColour(Ellipse element)
{
Random rnd = new Random();
int red = rnd.Next(0, 255);
int green = rnd.Next(0, 255);
int blue = rnd.Next(0, 255);
Brush fillColour = new SolidColorBrush(Color.FromRgb((byte)red, (byte)green, (byte)blue));
return fillColour;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
List<Ellipse> elements = new List<Ellipse>();
elements.Add(Circle1);
elements.Add(Circle2);
elements.Add(Circle3);
elements.Add(Circle4);
elements.Add(Circle5);
elements.Add(Circle6);
elements.Add(Circle7);
elements.Add(Circle8);
Random rnd = new Random();
foreach (Ellipse element in elements)
{
int r = rnd.Next(1, 10);
double duration = (Double)r / 10;
Appear(element, duration, SetEllipseColour(element));
}
}
And my WPF:
<Canvas Margin="10">
<Ellipse
x:Name="Circle1"
Fill="Black"
Height="100"
Width="100"/>
<Ellipse
x:Name="Circle2"
Fill="Black"
Height="100"
Width="100"
Margin="120,0,0,0"/>
<Ellipse
x:Name="Circle3"
Fill="Black"
Height="100"
Width="100"
Margin="240,0,0,0"/>
<Ellipse
x:Name="Circle4"
Fill="Black"
Height="100"
Width="100"
Margin="360,0,0,0"/>
<Ellipse
x:Name="Circle5"
Fill="Black"
Height="100"
Width="100"
Margin="0,120,0,0"/>
<Ellipse
x:Name="Circle6"
Fill="Black"
Height="100"
Width="100"
Margin="120,120,0,0"/>
<Ellipse
x:Name="Circle7"
Fill="Black"
Height="100"
Width="100"
Margin="240,120,0,0"/>
<Ellipse
x:Name="Circle8"
Fill="Black"
Height="100"
Width="100"
Margin="360,120,0,0"/>
</Canvas>
<Button x:Name="button1" Content="Start" Width="80" Height="20" Margin="0,200,0,0" Click="button1_Click"/>
Note: I know I can compress / change my code to make it neater or better, but for now I just want to get the colours working.
So currently the code I have changes the fill colour of all Ellipse elements, but I want to change it to just affect each Circle. How would I go about doing this?
Edit:
For those who are confused what Im trying to ask, I do not know how to individually change the fill colour of every Circle.
Set the instance of the Random class at the class level, check the below example, by clicking on the the Button that says Blink Em! the animation is triggered.
The XAML
<Window x:Class="Blink.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:Blink"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" WindowState="Maximized">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button x:Name="BlinkEm" Content="Blink Em!" Height="30" Click="BlinkEm_Click"/>
<StackPanel x:Name="Container" Orientation="Horizontal" Loaded="Container_Loaded" Grid.Row="1"/>
</Grid>
</Window>
The code-behind
namespace Blink
{
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public Random random = new Random();
public MainWindow()
{
InitializeComponent();
}
public List<Ellipse> CreateCircles(int count)
{
List<Ellipse> circles = new List<Ellipse>();
for (int i = 0; i < count; i++)
{
var circle = new Ellipse
{
Height = 100,
Width = 100,
Margin = new Thickness(10),
Fill = Brushes.SkyBlue
};
circles.Add(circle);
}
return circles;
}
public void AddCircles()
{
var circles = this.CreateCircles(8);
foreach (var circle in circles)
{
Container.Children.Add(circle);
}
}
private void Container_Loaded(object sender, RoutedEventArgs e)
{
AddCircles();
}
private void BlinkEm_Click(object sender, RoutedEventArgs e)
{
foreach (Ellipse circle in Container.Children)
{
circle.Fill = GetRandomColor();
circle.BeginAnimation(Ellipse.OpacityProperty, GetBlinkAnimation());
}
}
public Brush GetRandomColor()
{
var red = Convert.ToByte(random.Next(0, 255));
var green = Convert.ToByte(random.Next(0, 255));
var blue = Convert.ToByte(random.Next(0, 255));
return new SolidColorBrush(Color.FromRgb(red, green, blue));
}
public DoubleAnimation GetBlinkAnimation()
{
var duration = random.NextDouble();
var animation = new DoubleAnimation
{
From = 0.0,
To = 1.0,
Duration = new Duration(TimeSpan.FromSeconds(duration)),
RepeatBehavior = RepeatBehavior.Forever
};
return animation;
}
}
}
I think your mistake here is that the Random object is being recreated every time. Try putting that into a field and initializing it once, or by sending it as a parameter to your SetEllipseColor method.
Due to random number generators not actually being random, they derive their "initial" random values from a seed value, often the current time. This means if you create a bunch of new Random instances in a very short time, they are likely to end up with the same seed and thus the same value.
(In more general terms, "Appear" and "SetEllipseColor" aren't very good method names. The former is vague and the latter doesn't actually describe what that method is doing.)
I'm experimenting with the concept of drawing grid lines over a control and was wondering what adjustments I might need to make to make this actually work. I found some code on another post that enables grid lines to be drawn OnRender over a canvas. Here's what that looks like:
public class MyCanvas : Canvas
{
public bool IsGridVisible = true;
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
base.OnRender(dc);
if (IsGridVisible)
{
// Draw GridLines
Pen pen = new Pen(Brushes.Black, 1);
pen.DashStyle = DashStyles.Solid;
for (double x = 0; x < this.ActualWidth; x += 2)
{
dc.DrawLine(pen, new Point(x, 0), new Point(x, this.ActualHeight));
}
for (double y = 0; y < this.ActualHeight; y += 2)
{
dc.DrawLine(pen, new Point(0, y), new Point(this.ActualWidth, y));
}
}
}
public MyCanvas()
{
DefaultStyleKey = typeof(MyCanvas);
}
}
This part: y += 2 indicates how many other pixels/points to wait before drawing next line, though I am uncertain of it's correctness.
Here's the xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer>
<local:MyCanvas>
<local:MyCanvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="2" Canvas.Left="2" Source="C:\Users\Me\Pictures\nyan-wallpaper2.jpg" Width="325" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
</ScrollViewer>
<Slider x:Name="Slider" Maximum="500" Grid.Row="1" Value="1"/>
</Grid>
Here are screenshots of what the above results in.
As you can see, the grid lines change in size as you zoom and the lines themselves do not snap around each individual pixel. I highlighted an example pixel in red to show how small the lines should be versus how they actually are.
I read that the thickness of the pen should be divided by the scale value, however, I tested this by replacing Pen pen = new Pen(Brushes.Black, 1); with Pen pen = new Pen(Brushes.Black, 1 / 3); and set the ScaleX and ScaleY of MyCanvas to 3. At that point, no lines showed at all.
Any help at all is immensely valued!
Got it working like this for anyone curious:
MainWindow.xaml.cs
namespace Test
{
public class MyCanvas : Canvas
{
public bool IsGridVisible = false;
#region Dependency Properties
public static DependencyProperty ZoomValueProperty = DependencyProperty.Register("ZoomValue", typeof(double), typeof(MyCanvas), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnZoomValueChanged));
public double ZoomValue
{
get
{
return (double)GetValue(ZoomValueProperty);
}
set
{
SetValue(ZoomValueProperty, value);
}
}
private static void OnZoomValueChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
}
#endregion
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
base.OnRender(dc);
IsGridVisible = ZoomValue > 4.75 ? true : false;
if (IsGridVisible)
{
// Draw GridLines
Pen pen = new Pen(Brushes.Black, 1 / ZoomValue);
pen.DashStyle = DashStyles.Solid;
for (double x = 0; x < this.ActualWidth; x += 1)
{
dc.DrawLine(pen, new Point(x, 0), new Point(x, this.ActualHeight));
}
for (double y = 0; y < this.ActualHeight; y += 1)
{
dc.DrawLine(pen, new Point(0, y), new Point(this.ActualWidth, y));
}
}
}
public MyCanvas()
{
DefaultStyleKey = typeof(MyCanvas);
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private WriteableBitmap bitmap = new WriteableBitmap(500, 500, 96d, 96d, PixelFormats.Bgr24, null);
private void Button_Click(object sender, RoutedEventArgs e)
{
int size = 1;
Random rnd = new Random(DateTime.Now.Millisecond);
bitmap.Lock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests.
for (int y = 0; y < 500; y++)
{
for (int x = 0; x < 500; x++)
{
byte colR = (byte)rnd.Next(256);
byte colG = (byte)rnd.Next(256);
byte colB = (byte)rnd.Next(256);
DrawRectangle(bitmap, size * x, size * y, size, size, Color.FromRgb(colR, colG, colB));
}
}
bitmap.Unlock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests.
Image.Source = bitmap; // This should be done only once
}
public void DrawRectangle(WriteableBitmap writeableBitmap, int left, int top, int width, int height, Color color)
{
// Compute the pixel's color
int colorData = color.R << 16; // R
colorData |= color.G << 8; // G
colorData |= color.B << 0; // B
int bpp = writeableBitmap.Format.BitsPerPixel / 8;
unsafe
{
for (int y = 0; y < height; y++)
{
// Get a pointer to the back buffer
int pBackBuffer = (int)writeableBitmap.BackBuffer;
// Find the address of the pixel to draw
pBackBuffer += (top + y) * writeableBitmap.BackBufferStride;
pBackBuffer += left * bpp;
for (int x = 0; x < width; x++)
{
// Assign the color data to the pixel
*((int*)pBackBuffer) = colorData;
// Increment the address of the pixel to draw
pBackBuffer += bpp;
}
}
}
writeableBitmap.AddDirtyRect(new Int32Rect(left, top, width, height));
}
}
}
MainWindow.xaml
<Window x:Class="Test.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:Test"
mc:Ignorable="d"
Title="MainWindow"
Height="Auto"
Width="Auto"
WindowStartupLocation="CenterScreen"
WindowState="Maximized">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer>
<local:MyCanvas ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyCanvas.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="1" Canvas.Left="1" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
</ScrollViewer>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Slider x:Name="Slider" Maximum="100" Minimum="0.5" Value="1" Width="200"/>
<Button Click="Button_Click" Content="Click Me!"/>
</StackPanel>
</Grid>
</Window>
We generate a bitmap with random colored pixels and then render the grid lines only if zoomed up close. Performance-wise, this is actually better than expected. I should note, though, that if you attempt to zoom below 50%, the app crashes. Not sure if it's an issue with the grid lines being drawn at a minute size (IsGridVisible = true where ZoomValue < 0.5) or with the bitmap being generated. Either way, cheers!
Update
Didn't realize the grid lines are still behind the contents of the canvas. Haven't worked out a solution for that yet...
Update 2
Replace:
<local:MyCanvas ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyCanvas.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyCanvas.LayoutTransform>
<Image Canvas.Top="1" Canvas.Left="1" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</local:MyCanvas>
With:
<Grid>
<Canvas>
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</Canvas.LayoutTransform>
<Image Canvas.Top="5" Canvas.Left="5" x:Name="Image" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</Canvas>
<local:MyGrid ZoomValue="{Binding ElementName=ScaleTransform, Path=ScaleX}">
<local:MyGrid.LayoutTransform>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ElementName=Slider, Path=Value}" ScaleY="{Binding ElementName=Slider, Path=Value}"/>
</local:MyGrid.LayoutTransform>
</local:MyGrid>
</Grid>
I believe another boost in performance as we are utilizing a simpler control to display the grid lines, plus, the grid lines can be placed either below or above desired controls.
Update 3
I have decided to post my latest solution, which is significantly more efficient and can all be done in XAML:
<Grid>
<Grid.Background>
<DrawingBrush Viewport="0,0,5,5" ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M-.5,0 L50,0 M0,10 L50,10 M0,20 L50,20 M0,30 L50,30 M0,40 L50,40 M0,0 L0,50 M10,0 L10,50 M20,0 L20,50 M30,0 L30,50 M40,0 L40,50">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Grid.Background>
</Grid>
I have added a map in my Windows Phone 8 application. Now I want is to view one desired point with a center point on the map.
XAML:
<Grid x:Name="ContentPanel" Grid.RowSpan="2">
<Grid.RowDefinitions>
<RowDefinition Height="68" />
<RowDefinition Height="1" />
<RowDefinition Height="65" />
<RowDefinition Height="1" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<maps:Map x:Name="map"
Grid.RowSpan="5"
Height="800" />
<Image Source="/Assets/Images/Pin.png" Width="35" Height="55"
VerticalAlignment="Center" HorizontalAlignment="Center"
Grid.RowSpan="5"
Canvas.ZIndex="15"/>
<Button Grid.Row="0" Content="Search" />
<Button Grid.Row="4" Content="Check" />
</Grid>
View Model C#
MapRectLocation zoomLocation = new MapRectLocation();
zoomLocation.CenterPoint = new Location() { lat = CenterLocation.Latitude, lng = CenterLocation.Longitude };
zoomLocation.Locationx = new Location();
zoomLocation.Locationx.Latitude = FirstLocation.Latitude;
zoomLocation.Locationx.Longitude= FirstLocation.Longitude;
// Calculate the other point for boundary
zoomLocation.Locationy= GetEqivalentPoint(CenterLocation, FirstLocation);
public Location GetEqivalentPoint(System.Device.Location.GeoCoordinate CenterLocation, Location location)
{
var dlat = CenterLocation.Latitude - location.lat;
var dlng = CenterLocation.Longitude - location.lng;
Location equiPoint = new Location();
equiPoint.lat = CenterLocation.Latitude + dlat;
equiPoint.lng = CenterLocation.Longitude + dlng;
return equiPoint;
}
XAML.cs Code:
List<GeoCoordinate> zoomBoundaries = new List<GeoCoordinate>();
zoomBoundaries.Add(new GeoCoordinate(mapViewLocation.Locationx.lat, mapViewLocation.Locationx.lng));
zoomBoundaries.Add(new GeoCoordinate(mapViewLocation.Locationy.lat, mapViewLocation.Locationy.lng));
map.SetView(LocationRectangle.CreateBoundingRectangle(zoomBoundaries), new Thickness(0, 150, 0, 300));
The map sets to desired zoom level to display that point but the problem is it does not keep the same centerpoint as earlier. I want to keep the Centerlocation as same and display FirstLocation point on the map. I am also setting margin in Setview as to keep that First location point above the 2 buttons that I have in my XAML code overlaying map.
Please let me know how to rectify this problem?
Try this:
void SetLoc()
{
MyMap.Layers.Clear();
try
{
// ... get the coordinates "myGeoCoordinate"
// Make my current location the center of the Map.
this.MyMap.Center = myGeoCoordinate;
this.MyMap.ZoomLevel = 12;
// Create a small circle to mark the current location.
Ellipse myCircle = new Ellipse();
myCircle.Fill = new SolidColorBrush(Colors.Blue);
myCircle.Height = 20;
myCircle.Width = 20;
myCircle.Opacity = 50;
// Create a MapOverlay to contain the circle.
MapOverlay myLocationOverlay = new MapOverlay();
myLocationOverlay.Content = myCircle;
myLocationOverlay.PositionOrigin = new Point(0.5, 0.5);
myLocationOverlay.GeoCoordinate = myGeoCoordinate;
// Create a MapLayer to contain the MapOverlay.
MapLayer myLocationLayer = new MapLayer();
myLocationLayer.Add(myLocationOverlay);
// Add the MapLayer to the Map.
MyMap.Layers.Add(myLocationLayer);
}
catch
{
}
}
Sorry if the title a bit misleading, I have difficulty to entitle it. It's also not exactly "center". (see below for further explanations)
Hi, I am going to make a project to write numbered musical scores. But I found an obstacle on leveling the "font". (See below picture for visual)
I need 1 2 3 3 1 2 3 4 4 to be in a straight "line"
I use WrapPanel as the container of my notes (as in the screen).
The main problem is on the pitch dot, which located EITHER above or below the note. Pitch is bound to the note, so I need process it in 1 User Control. But, if so, then I can't control the placement of the note "font" to be in one line.
Below is my note user control code :
XAML
<UserControl x:Class="RevisiConverter.NumericNoteBox"
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" Width="{Binding ActualWidth, ElementName=txbNote}"
Height="{Binding ActualHeight, ElementName=mainStack}">
<StackPanel Name="mainStack">
<StackPanel Name="topStack">
</StackPanel>
<Canvas Name="canvas" Width="{Binding ActualWidth, ElementName=txbNote}" Height="{Binding ActualHeight, ElementName=txbNote}"
HorizontalAlignment="Center">
<TextBlock Name="txbNote" Text="1" Foreground="Black" FontSize="15"
Margin="0,0,0,-3" FontFamily="Courier" Canvas.Top="0" Canvas.Left="0"/>
</Canvas>
<StackPanel Name="botStack">
</StackPanel>
</StackPanel>
XAML.CS
public partial class NumericNoteBox : UserControl
{
private Note _child;
private Line _sharp;
public Note Child
{
get { return _child; }
set
{
_child = value;
Update();
}
}
public NumericNoteBox()
{
InitializeComponent();
_sharp = null;
}
public void Update()
{
txbNote.Text = _child.MainNote.ToString();
if (_sharp != null)
{
_sharp.Visibility = Visibility.Hidden;
}
topStack.Children.Clear();
botStack.Children.Clear();
if (_child != null)
{
if (_child.Pitch > 0)
{
for (int i = 0; i < _child.Pitch; i++)
{
topStack.Children.Add(new Ellipse());
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Width = 3;
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Height = 3;
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Fill = Brushes.Black;
if (_child.Accidental != Note.Accidentals.Flat)
{
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Margin = new Thickness(0, 1, 0, 0);
}
else
{
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Margin = new Thickness(8, 1, 0, 0);
}
}
}
else if (_child.Pitch < 0)
{
for (int i = 0; i < Math.Abs(_child.Pitch); i++)
{
botStack.Children.Add(new Ellipse());
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Width = 3;
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Height = 3;
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Fill = Brushes.Black;
if (_child.Accidental != Note.Accidentals.Flat)
{
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Margin = new Thickness(0, 1, 0, 0);
}
else
{
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Margin = new Thickness(8, 1, 0, 0);
}
}
}
if (_child.Accidental == Note.Accidentals.Flat)
{
txbNote.Text = "b" + _child.MainNote.ToString();
}
else if (_child.Accidental == Note.Accidentals.Sharp)
{
if (_sharp == null)
{
_sharp = new Line();
_sharp.X1 = 10;
_sharp.Y1 = 2.5;
_sharp.X2 = -2.5;
_sharp.Y2 = 12.5;
_sharp.StrokeThickness = 1;
_sharp.Stroke = Brushes.Black;
canvas.Children.Add(_sharp);
}
_sharp.Visibility = Visibility.Visible;
}
}
}
}
Note :
I'm not bound to that code, so if any of you have dealt to things like this more efficiently with totally different approach, then it's always welcome.
Sorry for some grammar error, if any, since english is not my first language.
I feel that I have not explained my problem real clear, since I also quite confused on it, so, please clarify anything you need.
Thanks
ADDITIONAL DETAILS :
The dot theoretically doesn't have certain limitation, but in practice, it's usually only 3 maximum (either 3 on top or 3 on bottom - not both)
In my code above, the note user control is divided into 3 grid row, the top one is for stacking the top dot, the mid one is for visualizing the note (numbers) and the bot one is for stacking the bot dot.
Please clarify anything, and I'll add more.
You should do this with an ItemsControl which use an appropriate ItemTemplate for the numeric notes.
First, create a ViewModel that defines a collection of "numeric note" items:
public class NumericNoteItem
{
public int Number { get; set; }
public int Pitch { get; set; }
}
public class ViewModel
{
public ViewModel()
{
NumericNotes = new ObservableCollection<NumericNoteItem>();
}
public ObservableCollection<NumericNoteItem> NumericNotes { get; private set; }
}
In the constructor of your MainWindow, you may set the DataContext to an instance of this ViewModel like this:
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.NumericNotes.Add(new NumericNoteItem { Number = 1, Pitch = 0 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 2, Pitch = 1 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 3, Pitch = -1 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 4, Pitch = 0 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 5, Pitch = 2 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 6, Pitch = -2 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 4, Pitch = 0 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 5, Pitch = 3 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 6, Pitch = -3 });
DataContext = vm;
}
For visualizing the pitch I'd recommend to use a Path object with a Geometry that consists of a number of EllipseGeometries. To achieve this you would implement a binding converter that converts the pitch number into a Geometry, like shown below. It uses the parameter argument of the Convert method to create a Geometry for either positive or negative pitch values.
public class NotePitchConverter : IValueConverter
{
private const double radius = 1.5;
private const double distance = 2 * radius + 1;
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var pitch = (int)value;
var geometry = new GeometryGroup();
if (parameter as string == "Bottom")
{
pitch = -pitch;
}
for (int i = 0; i < pitch; i++)
{
geometry.Children.Add(new EllipseGeometry(
new Point(radius, radius + i * distance), radius, radius));
}
return geometry;
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Now, in XAML you would create an instance of this converter in e.g. the MainWindow Resources:
<Window.Resources>
<local:NotePitchConverter x:Key="NotePitchConverter"/>
</Window.Resources>
and write the ItemsControl like shown below. Note that the DataTemplate uses a fixed height for the Path elements that show the top and bottom pitch. If you need to show more than three dots, you'd need to increase their height.
<ItemsControl ItemsSource="{Binding NumericNotes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="12"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="12"/>
</Grid.RowDefinitions>
<Path Grid.Row="0" Fill="Black" HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Data="{Binding Pitch,
Converter={StaticResource NotePitchConverter}}"/>
<TextBlock Grid.Row="1" Text="{Binding Number}"/>
<Path Grid.Row="2" Fill="Black" HorizontalAlignment="Center"
VerticalAlignment="Top"
Data="{Binding Pitch,
Converter={StaticResource NotePitchConverter},
ConverterParameter=Bottom}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>