Я работаю с программой, которая использует DrawingVisual для создания динамического графика, и мне нужно добавить кнопку, чтобы сохранить график в файл в заданной точке, указанной пользователем. График имеет черный фон, но его нужно изменить на версию для печати с белым фоном и черными линиями, но я все еще застреваю при захвате объекта и сохранении его в файл. Задача кажется тривиальной, но я новичок в C #, поэтому, пожалуйста, потерпите меня.
Ниже приведен код определения DrawingContext, в котором, как мне кажется, нужно начинать с изменений.
namespace ATEGUI.Graphing.Presentation.CustomControls
{
public class Graph : Panel
{
private const double LogScaleSmallGridDivs = 10;
public double MinX
{
get { return (double)GetValue(MinXProperty); }
set { SetValue(MinXProperty, value); }
}
public static readonly DependencyProperty MinXProperty =
DependencyProperty.Register("MinX", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double MinY
{
get { return (double)GetValue(MinYProperty); }
set { SetValue(MinYProperty, value); }
}
public static readonly DependencyProperty MinYProperty =
DependencyProperty.Register("MinY", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double MaxX
{
get { return (double)GetValue(MaxXProperty); }
set { SetValue(MaxXProperty, value); }
}
public static readonly DependencyProperty MaxXProperty =
DependencyProperty.Register("MaxX", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double MaxY
{
get { return (double)GetValue(MaxYProperty); }
set { SetValue(MaxYProperty, value); }
}
public static readonly DependencyProperty MaxYProperty =
DependencyProperty.Register("MaxY", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public bool AutoScaleX
{
get { return (bool)GetValue(AutoScaleXProperty); }
set { SetValue(AutoScaleXProperty, value); }
}
public static readonly DependencyProperty AutoScaleXProperty =
DependencyProperty.Register("AutoScaleX", typeof(bool), typeof(Graph), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
public bool AutoScaleY
{
get { return (bool)GetValue(AutoScaleYProperty); }
set { SetValue(AutoScaleYProperty, value); }
}
public static readonly DependencyProperty AutoScaleYProperty =
DependencyProperty.Register("AutoScaleY", typeof(bool), typeof(Graph), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
public double LogScaleX
{
get { return (double)GetValue(LogScaleXProperty); }
set { SetValue(LogScaleXProperty, value); }
}
public static readonly DependencyProperty LogScaleXProperty =
DependencyProperty.Register("LogScaleX", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double LogScaleY
{
get { return (double)GetValue(LogScaleYProperty); }
set { SetValue(LogScaleYProperty, value); }
}
public static readonly DependencyProperty LogScaleYProperty =
DependencyProperty.Register("LogScaleY", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double Zoom
{
get { return (double)GetValue(ZoomProperty); }
set { SetValue(ZoomProperty, value); }
}
public static readonly DependencyProperty ZoomProperty =
DependencyProperty.Register("Zoom", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double ParentWidth
{
get { return (double)GetValue(ParentWidthProperty); }
set { SetValue(ParentWidthProperty, value); }
}
public static readonly DependencyProperty ParentWidthProperty =
DependencyProperty.Register("ParentWidth", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double ParentHeight
{
get { return (double)GetValue(ParentHeightProperty); }
set { SetValue(ParentHeightProperty, value); }
}
public static readonly DependencyProperty ParentHeightProperty =
DependencyProperty.Register("ParentHeight", typeof(double), typeof(Graph), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
public Brush Foreground
{
get { return (Brush)GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
public static readonly DependencyProperty ForegroundProperty =
DependencyProperty.Register("Foreground", typeof(Brush), typeof(Graph), new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender));
public Brush Grid
{
get { return (Brush)GetValue(GridProperty); }
set { SetValue(GridProperty, value); }
}
public static readonly DependencyProperty GridProperty =
DependencyProperty.Register("Grid", typeof(Brush), typeof(Graph), new FrameworkPropertyMetadata(Brushes.Gray, FrameworkPropertyMetadataOptions.AffectsRender));
public IEnumerable Graphs
{
get { return (IEnumerable)GetValue(GraphsProperty); }
set { SetValue(GraphsProperty, value); }
}
public static readonly DependencyProperty GraphsProperty =
DependencyProperty.Register("Graphs", typeof(IEnumerable), typeof(Graph), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, OnPropertyChanged));
public IEnumerable Labels
{
get { return (IEnumerable)GetValue(LabelsProperty); }
set { SetValue(LabelsProperty, value); }
}
public static readonly DependencyProperty LabelsProperty =
DependencyProperty.Register("Labels", typeof(IEnumerable), typeof(Graph), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, OnPropertyChanged));
private static void OnPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var graph = sender as Graph;
if (graph != null)
{
if (e.Property == GraphsProperty || e.Property == LabelsProperty)
graph.OnCollectionChanged(e.NewValue, e.OldValue);
}
}
public void OnCollectionChanged(object newValue, object oldValue)
{
if (newValue is INotifyCollectionChanged)
AddWeakEventListener((INotifyCollectionChanged)newValue, HandleCollectionChanged);
if (oldValue is INotifyCollectionChanged)
RemoveWeakEventListener((INotifyCollectionChanged)oldValue, HandleCollectionChanged);
}
private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
this.InvalidateVisual();
}
protected override Size MeasureOverride(Size availableSize)
{
return new Size(ParentWidth * Math.Max(Zoom, 1), ParentHeight * Math.Max(Zoom, 1));
}
/*
protected override Size ArrangeOverride(Size finalSize)
{
var size = base.ArrangeOverride(finalSize);
return new Size(size.Width * Math.Max(Zoom, 1), size.Height * Math.Max(Zoom, 1));
}*/
protected override void OnRender(DrawingContext dc)
{
if (ActualWidth > 0 && ActualHeight > 0)
{
var fontSizeValue = GetValue(TextElement.FontSizeProperty);
double fontSize = fontSizeValue is double ? (double)fontSizeValue : 12;
dc.DrawRectangle(Background, null, new Rect(0, 0, ActualWidth, ActualHeight));
DoAutoScale();
DrawAxis(dc, fontSize);
DrawGraphs(dc, fontSize);
DrawLabels(dc, fontSize);
}
}
private void DoAutoScale()
{
if (AutoScaleX)
{
//Compute Auto-scale
if (Graphs != null && Graphs.OfType<Model.Graph>().Any())
{
double min = Graphs.OfType<Model.Graph>().Select(t => t.Points.Select(p => p.X).Min()).Min();
double max = Graphs.OfType<Model.Graph>().Select(t => t.Points.Select(p => p.X).Max()).Max();
for (int i = 0; i < 2; i++)
{
double step = RoundLog10((max - min) / 10.0);
min = Math.Floor(min / step) * step;
max = Math.Ceiling(max / step) * step;
}
MinX = min;
MaxX = max;
}
else
{
MinX = 0;
MaxX = ActualWidth;
}
}
if (AutoScaleY)
{
//Compute Auto-scale
if (Graphs != null && Graphs.OfType<Model.Graph>().Any())
{
double min = Graphs.OfType<Model.Graph>().Select(t => t.Points.Select(p => p.Y).Min()).Min();
double max = Graphs.OfType<Model.Graph>().Select(t => t.Points.Select(p => p.Y).Max()).Max();
for (int i = 0; i < 2; i++)
{
double step = RoundLog10((max - min) / 10.0);
min = Math.Floor(min / step) * step;
max = Math.Ceiling(max / step) * step;
}
MinY = min;
MaxY = max;
}
else
{
MinY = 0;
MaxY = ActualHeight;
}
}
//make sure min and max are valid for log scaling
if (LogScaleX > 1.0)
{
if (MinX <= 0) MinX = 1;
if (MaxX <= 0) MaxX = 1;
}
if (LogScaleY > 1.0)
{
if (MinY <= 0) MinY = 1;
if (MaxY <= 0) MaxY = 1;
}
}
private void DrawAxis(DrawingContext dc, double fontSize)
{
var minorGrid = Grid.Clone();
minorGrid.Opacity = 0.25;
//draw y axis
if (LogScaleX > 1.0)
{
var minXLog = Math.Log(MinX, LogScaleX);
var maxXLog = Math.Log(MaxX, LogScaleX);
var stepXLog = Math.Max(1.0, Math.Ceiling((maxXLog - minXLog) / 10));
for (double i = minXLog; i < maxXLog; i += stepXLog)
{
var x = Math.Pow(LogScaleX, i);
DrawLine(dc, new Pen(Grid, 1), (Transform(x, MinY)), (Transform(x, MaxY)));
var x2 = Math.Pow(LogScaleX, i + stepXLog);
var w = x2 - x;
for (double j = 0; j < LogScaleSmallGridDivs; j++)
{
var x3 = x + w * j / LogScaleSmallGridDivs;
DrawLine(dc, new Pen(minorGrid, 1), (Transform(x3, MinY)), (Transform(x3, MaxY)));
}
}
double yAxisPos = Math.Max(Math.Min(0, MaxY), MinY);
DrawLine(dc, new Pen(Foreground, 2), (Transform(MinX, yAxisPos)), (Transform(MaxX, yAxisPos)));
for (double i = minXLog; i < maxXLog; i += stepXLog)
{
var x = Math.Pow(LogScaleX, i);
var p = Transform(x, yAxisPos);
DrawLine(dc, new Pen(Foreground, 1), (new Point(p.X, p.Y + 4)), (new Point(p.X, p.Y - 4)));
DrawText(dc, x.ToString(), fontSize, Foreground, new Point(p.X + 2, p.Y));
}
}
else
{
double stepX = RoundLog10((MaxX - MinX) / 10);
for (double x = Math.Floor(MinX / stepX) * stepX; x < MaxX; x += stepX)
DrawLine(dc, new Pen(Grid, 1), (Transform(x, MinY)), (Transform(x, MaxY)));
double yAxisPos = Math.Max(Math.Min(0, MaxY), MinY);
DrawLine(dc, new Pen(Foreground, 2), (Transform(MinX, yAxisPos)), (Transform(MaxX, yAxisPos)));
for (double x = Math.Floor(MinX / stepX) * stepX; x < MaxX; x += stepX)
{
var p = Transform(x, yAxisPos);
DrawLine(dc, new Pen(Foreground, 1), (new Point(p.X, p.Y + 4)), (new Point(p.X, p.Y - 4)));
DrawText(dc, (Math.Round(x / stepX) * stepX).ToString(), fontSize, Foreground, new Point(p.X + 2, p.Y));
}
}
//draw x axis
if (LogScaleY > 1.0)
{
var minYLog = Math.Log(MinY, LogScaleY);
var maxYLog = Math.Log(MaxY, LogScaleY);
var stepYLog = Math.Max(1.0, Math.Ceiling((maxYLog - minYLog) / 10));
for (double i = minYLog; i < maxYLog; i += stepYLog)
{
var y = Math.Pow(LogScaleY, i);
DrawLine(dc, new Pen(Grid, 1), (Transform(MinX, y)), (Transform(MaxX, y)));
var y2 = Math.Pow(LogScaleY, i + stepYLog);
var h = y2 - y;
for (double j = 0; j < LogScaleSmallGridDivs; j++)
{
var y3 = y + h * j / LogScaleSmallGridDivs;
DrawLine(dc, new Pen(minorGrid, 1), (Transform(MinX, y3)), (Transform(MaxX, y3)));
}
}
double xAxisPos = Math.Max(Math.Min(0, MaxX), MinX);
DrawLine(dc, new Pen(Foreground, 2), (Transform(xAxisPos, MinY)), (Transform(xAxisPos, MaxY)));
for (double i = minYLog; i < maxYLog; i += stepYLog)
{
var y = Math.Pow(LogScaleY, i);
var p = Transform(xAxisPos, y);
DrawLine(dc, new Pen(Foreground, 1), (new Point(p.X, p.Y + 4)), (new Point(p.X, p.Y - 4)));
DrawText(dc, y.ToString(), fontSize, Foreground, new Point(p.X + 2, p.Y));
}
}
else
{
double stepY = RoundLog10((MaxY - MinY) / 10);
for (double y = Math.Floor(MinY / stepY) * stepY; y < MaxY; y += stepY)
DrawLine(dc, new Pen(Grid, 1), (Transform(MinX, y)), (Transform(MaxX, y)));
double xAxisPos = Math.Max(Math.Min(0, MaxX), MinX);
DrawLine(dc, new Pen(Foreground, 2), (Transform(xAxisPos, MinY)), (Transform(xAxisPos, MaxY)));
for (double y = Math.Floor(MinY / stepY) * stepY; y < MaxY; y += stepY)
{
var p = Transform(xAxisPos, y);
DrawLine(dc, new Pen(Foreground, 1), (new Point(p.X + 4, p.Y)), (new Point(p.X - 4, p.Y)));
DrawText(dc, (Math.Round(y / stepY) * stepY).ToString(), fontSize, Foreground, new Point(p.X + 2, p.Y));
}
}
}
private void DrawLabels(DrawingContext dc, double fontSize)
{
if (Labels != null)
{
foreach (Model.Label label in Labels)
{
var brush = string.IsNullOrEmpty(label.Color) ? Foreground : TypeDescriptor.GetConverter(typeof(Brush)).ConvertFromString(label.Color) as Brush;
if (brush == null)
brush = Foreground;
dc.DrawText(new FormattedText(label.Text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, GetTypeface(), fontSize, brush), Transform(label.Location));
}
}
}
private void DrawGraphs(DrawingContext dc, double fontSize)
{
Point mousePos = InverseTransform(Mouse.GetPosition(this));
if (Graphs != null)
{
foreach (Model.Graph graph in Graphs)
{
var brush = string.IsNullOrEmpty(graph.Color) ? Foreground : TypeDescriptor.GetConverter(typeof(Brush)).ConvertFromString(graph.Color) as Brush;
if (brush == null)
brush = Foreground;
var pen = new Pen(brush, graph.Weight);
if (graph.Points.Count == 1)
{
double weightX = graph.Weight;
double weightY = graph.Weight;
dc.DrawRectangle(brush, null, new Rect(Transform(graph.Points.First()).X - weightX / 2, Transform(graph.Points.First()).Y - weightY / 2, weightX, weightY));
}
else
{
var figure = new PathFigure(Transform(graph.Points.First()), new[] { new PolyLineSegment(graph.Points.Select(t => Transform(t)), true) }, false);
dc.DrawGeometry(null, pen, new PathGeometry(new[] { figure }));
}
//draw mouse loc
if (IsMouseOver)
{
var closestPoint = graph.Points.FirstOrDefault();
double minDistance = Double.PositiveInfinity;
foreach (var point in graph.Points)
{
double distance = Math.Abs(mousePos.X - point.X);
if (distance < minDistance)
{
minDistance = distance;
closestPoint = point;
}
}
var closestPointTransform = Transform(closestPoint);
DrawLine(dc, new Pen(Grid, 0.5), (Transform(closestPoint.X, MinY)), (Transform(closestPoint.X, MaxY)));
DrawLine(dc, new Pen(Grid, 0.5), (Transform(MinX, closestPoint.Y)), (Transform(MaxX, closestPoint.Y)));
dc.DrawEllipse(Foreground, null, closestPointTransform, graph.Weight, graph.Weight);
dc.DrawEllipse(null, pen, closestPointTransform, graph.Weight * 3, graph.Weight * 3);
dc.DrawText(new FormattedText(string.Format("X:{0}\nY:{1}", closestPoint.X, closestPoint.Y), CultureInfo.CurrentCulture, FlowDirection.LeftToRight, GetTypeface(), fontSize, brush), new Point(closestPointTransform.X + graph.Weight * 3, closestPointTransform.Y + graph.Weight * 3));
}
}
}
}
private void DrawText(DrawingContext dc, string text, double size, Brush brush, Point point)
{
var ft = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, GetTypeface(), size, brush);
var origin = point;
if (point.Y + ft.Height > ActualHeight)
origin = new Point(origin.X, origin.Y - ft.Height);
if (point.X + ft.Width > ActualWidth)
origin = new Point(origin.X - ft.Width, origin.Y);
dc.DrawText(ft, origin);
}
private void DrawLine(DrawingContext dc, Pen pen, Point p1, Point p2)
{
double halfPenWidth = pen.Thickness / 2.0;
dc.PushGuidelineSet(new GuidelineSet(new double[] { p1.X + halfPenWidth, p2.X + halfPenWidth }, new double[] { p1.Y + halfPenWidth, p2.Y + halfPenWidth }));
dc.DrawLine(pen, p1, p2);
dc.Pop();
}
private double Distance(Point p1, Point p2)
{
return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}
private Typeface GetTypeface()
{
return new Typeface(TextElement.GetFontFamily(this), TextElement.GetFontStyle(this), TextElement.GetFontWeight(this), TextElement.GetFontStretch(this));
}
private double RoundLog10(double x)
{
var nearestPow10 = Math.Pow(10, Math.Floor(Math.Log10(x)));
return Math.Floor(x / nearestPow10) * nearestPow10;
}
private Point Transform(Point p)
{
return Transform(p.X, p.Y);
}
private Point Transform(double x, double y)
{
double newX, newY;
if (LogScaleX > 1.0)
{
if (x <= 0) newX = 0;
else newX = ActualWidth * (Math.Log(x, LogScaleX) - Math.Log(MinX, LogScaleX)) / (Math.Log(MaxX, LogScaleX) - Math.Log(MinX, LogScaleX));
}
else
{
newX = ActualWidth * (x - MinX) / (MaxX - MinX);
}
if (LogScaleY > 1.0)
{
if (y <= 0) newY = ActualHeight;
else newY = ActualHeight * (Math.Log(MaxY, LogScaleY) - Math.Log(y, LogScaleY)) / (Math.Log(MaxY, LogScaleY) - Math.Log(MinY, LogScaleY));
}
else
{
newY = ActualHeight * (MaxY - y) / (MaxY - MinY);
}
return new Point(newX, newY);
}
private Point InverseTransform(Point p)
{
return InverseTransform(p.X, p.Y);
}
private Point InverseTransform(double x, double y)
{
double newX, newY;
if (LogScaleX > 1.0)
{
newX = Math.Pow(LogScaleX, (Math.Log(MaxX, LogScaleX) - Math.Log(MinX, LogScaleX)) * x / ActualWidth + Math.Log(MinX, LogScaleX));
}
else
{
newX = (MaxX - MinX) * x / ActualWidth + MinX;
}
if (LogScaleY > 1.0)
{
newY = Math.Pow(LogScaleY, Math.Log(MaxY, LogScaleY) - (Math.Log(MaxY, LogScaleY) - Math.Log(MinY, LogScaleY)) * y / ActualHeight);
}
else
{
newY = MaxY - (MaxY - MinY) * y / ActualHeight;
}
return new Point(newX, newY);
}
protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e)
{
this.InvalidateVisual();
base.OnMouseMove(e);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
this.InvalidateVisual();
base.OnMouseLeave(e);
}
#region WeakEventListener
private class CollectionChangedEventListener : IWeakEventListener
{
private readonly INotifyCollectionChanged source;
private readonly NotifyCollectionChangedEventHandler handler;
public CollectionChangedEventListener(INotifyCollectionChanged source, NotifyCollectionChangedEventHandler handler)
{
if (source == null) { throw new ArgumentNullException("source"); }
if (handler == null) { throw new ArgumentNullException("handler"); }
this.source = source;
this.handler = handler;
}
public INotifyCollectionChanged Source { get { return source; } }
public NotifyCollectionChangedEventHandler Handler { get { return handler; } }
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
handler(sender, (NotifyCollectionChangedEventArgs)e);
return true;
}
}
[NonSerialized]
private readonly List<CollectionChangedEventListener> collectionChangedListeners = new List<CollectionChangedEventListener>();
/// <summary>
/// Adds a weak event listener for a CollectionChanged event.
/// </summary>
/// <param name="source">The source of the event.</param>
/// <param name="handler">The event handler.</param>
/// <exception cref="ArgumentNullException">source must not be <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">handler must not be <c>null</c>.</exception>
protected void AddWeakEventListener(INotifyCollectionChanged source, NotifyCollectionChangedEventHandler handler)
{
if (source == null) { throw new ArgumentNullException("source"); }
if (handler == null) { throw new ArgumentNullException("handler"); }
CollectionChangedEventListener listener = new CollectionChangedEventListener(source, handler);
collectionChangedListeners.Add(listener);
CollectionChangedEventManager.AddListener(source, listener);
}
/// <summary>
/// Removes the weak event listener for a CollectionChanged event.
/// </summary>
/// <param name="source">The source of the event.</param>
/// <param name="handler">The event handler.</param>
/// <exception cref="ArgumentNullException">source must not be <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">handler must not be <c>null</c>.</exception>
protected void RemoveWeakEventListener(INotifyCollectionChanged source, NotifyCollectionChangedEventHandler handler)
{
if (source == null) { throw new ArgumentNullException("source"); }
if (handler == null) { throw new ArgumentNullException("handler"); }
CollectionChangedEventListener listener = collectionChangedListeners.LastOrDefault(l =>
l.Source == source && l.Handler == handler);
if (listener != null)
{
collectionChangedListeners.Remove(listener);
CollectionChangedEventManager.RemoveListener(source, listener);
}
}
#endregion
}
}