Archive for the ‘WPF’ Category

First Pull-request created.

Well, it had to happen some day.

I’ve found an issue in a project we are using @ work. I’ve created a fix and I want to share it!.

So this project is hosted on github, and it is indeed simple to add fixes.

Just follow these steps :

  1. Fork the project
  2. Create a feature / fix branch
  3. Apply fix.
  4. Push to github.
  5. View this branch on github
  6. Push the “Create Pull Request” button

It will then show you if it can be merged at all. Add some information and you are done!

better details

The fix.

I’ve created a rather simple fix. We are using the “Fluent.Ribbon” for a modern look and feel. But some of our controls are still winforms. There is nothing wrong with winforms, but a mixture of wpf and winforms is not always ideal.

Anyway, we have a combobox in our ribbon and when a winforms control has focus and you try to open this combobox in your ribbon, it acts up.  repro : 

MainWindow_2015-04-09_11-25-11

We have found this message in the debug window:

System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=IsDropDownOpen; DataItem=’ComboBox’ (Name=’StyleCbx’); target element is ‘ToggleButton’ (Name=’ToggleButton’); target property is ‘IsChecked’ (type ‘Nullable`1′) NullReferenceException:’System.NullReferenceException: Object reference not set to an instance of an object.
at Fluent.ComboBox.OnDropDownOpened(EventArgs e)
at System.Windows.Controls.ComboBox.OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
at MS.Internal.Data.PropertyPathWorker.SetValue(Object item, Object value)
at MS.Internal.Data.ClrBindingWorker.UpdateValue(Object value)
at System.Windows.Data.BindingExpression.UpdateSource(Object value)’

Turns out that the fix was rather simple. The combobox of fluent expected the Keyboard.FocusedElement, to always be non-null.

Well this is not the case when dealing with a mixed technology window. So the fix was created and a pull request has been offered.

 

 

DrawCurve / AddCurve for WPF / Cardinal Spline

For my project I need to draw Cardinal splines. Preferable the same way I was used to with the System.Drawing.Graphics or System.Drawing.Drawing2D.GraphicsPath. Not knowing much about splines, I noticed the number of PathPoints on a graphics path. The curve is drawn with beziers, so it’s some sort of string of beziers that make up this cardinal spline.

This project results in the same curves as you are used to with the graphics object (but this works with doubles not with floats). Tension and open and closed params are supported!

Anyway long story short, this project calculates control points for beziers. The curve will be draw with beziers, thus the outcome is exactly the same as with the Graphics object. I’ve seen other examples drawing the curve by them self calculating 100’s of points on the bezier it self .

Yes the graphics object does the same thing see msdn (quote:”Remarks The user must keep the original points if they are needed. The original points are converted to cubic Bézier control points internally, therefore there is no mechanism for returning the original points.”) .

If you draw a curve with 3 points, 7 points are needed to draw the curve, thus a performance gain(? not measured!) without losing the nice smoothness of the curve.

here is a screen shot:

cardinalsplineclosedexample

and here is the code

comments are welcome, Can u use this, or are you missing something?

ArcTo / ArcSegment like GraphicsPath.AddArc

This article gives a simpler interface (facade) to use the ArcTo Method… Providing a GraphicsPath AddArc like method.

public static GeometryDrawing GetArc(Rect rect, Angle start, Angle sweep){}

Today I implemented an adapter for GDI+ and WPF to draw similar objects. Simple ons like line, rectangle, ellipse,.. but I kinda struggled with arc??!!

public abstract void ArcTo(
	Point point,
	Size size,
	double rotationAngle,
	bool isLargeArc,
	SweepDirection sweepDirection,
	bool isStroked,
	bool isSmoothJoin
)

This is an Api that has way to much parameters if you ask me. All I need (and want) was a api like the AddArc on the GDI+ GraphicsPath which looks like :

public void AddArc(
	RectangleF rect,
	float startAngle,
	float sweepAngle
)

Which is all I need anyway.

In My Code I’m using the ArcTo method on a StreamGeometryContext instance (ArcSegment has a similair api so I’mmentioning it). First you have to move to the first point (which is done by calling BeginFigure) then you have to add a second point. Next argument Size has an interesting description on the MSDN documentation:

The width and height of an oval whose perimeter is used to draw the angle. If the oval is very rounded in all directions, the arc will be rounded, if it is nearly flat, so will the arc. For example, a very large width and height would represent a very large oval, which would give a slight curvature for the angle.

Ehh.. right.. how about just saying radius?

The next argument, rotationangle, can be done with a Matrix transform (rotate),.. so why it it there?

well the others make sense…

Well here is my facade to the ArcTo:

public static GeometryDrawing GetArc(Rect rect, Angle start, Angle sweep)

the way i like it,.. nice and simple.

public static GeometryDrawing GetArc(Rect rect, Angle start, Angle sweep)
        {
            GeometryDrawing ret = new GeometryDrawing();
            StreamGeometry geo = new StreamGeometry();

            //Set correct parameters
            SweepDirection sweepDir = sweep.Degrees < 0 ? SweepDirection.Counterclockwise : SweepDirection.Clockwise;
            bool isLargeArc = Math.Abs(sweep.Degrees) > 180;

            double cx = rect.Width / 2;
            double cy = rect.Height / 2;
            //Calculate start point
            double x1 = rect.X + cx + (Math.Cos(start.Radians) * cx);
            double y1 = rect.Y + cy + (Math.Sin(start.Radians) * cy);
            //Calculate end point
            double x2 = rect.X + cx + (Math.Cos(start.Radians + sweep.Radians) * cx);
            double y2 = rect.Y + cy + (Math.Sin(start.Radians + sweep.Radians) * cy);

            using (StreamGeometryContext ctx = geo.Open())
            {
                ctx.BeginFigure(new Point(x1, y1), false, false);
                ctx.ArcTo(new Point(x2, y2), new Size(cx,cy), 0, isLargeArc, sweepDir, true, false);
            }

            ret.Geometry = geo;
            return ret;
        }

!NOTE! The Angle class is just a simple converter form degrees to radians Nothing to worry about….

Here you can download a sample solution to get the demo working.

Or use this Extention Method in a static class..

public static void AddArc(this StreamGeometryContext ctx, Rect rect, double startRad, double sweepRad)
{
SweepDirection sweepDir = sweepRad < 0 ? SweepDirection.Counterclockwise : SweepDirection.Clockwise;
bool isLargeArc = Math.Abs(sweepRad) > Math.PI;
double cx = rect.Width / 2;
double cy = rect.Height / 2;
double x1 = rect.X + cx + (Math.Cos(startRad) * cx);
double y1 = rect.Y + cy + (Math.Sin(startRad) * cy);
double x2 = rect.X + cx + (Math.Cos(startRad + sweepRad) * cx);
double y2 = rect.Y + cy + (Math.Sin(startRad + sweepRad) * cy);
ctx.BeginFigure(new Point(x1, y1), false, false);
ctx.ArcTo(new Point(x2, y2), new Size(cx, cy), 0, isLargeArc, sweepDir, true, false);
}