Welcome to MSDN Blogs Sign in | Join | Help

Well, I just had it pointed out to me, that my blog is now listed on http://expression.microsoft.com. I'm actually pretty excited to see myself up there. It does, however, make me realize that I should spend a little time sprucing up the look of my blog.

The current theme is something like "Fire", which I chose because of my love for the elements. You can figure it out... a thought of 'hot software' and the like. But, I'm not saying that Blend isn't 'hot' any more... just that I need to figure out a new 'feel' for my blog.

So, if it seems to change a couple times in the near future, it's just me getting a feel for new looks and such. I will continue to post new thoughts and ideas as I go along. Now that the big thing I was working on (which, naturally, I can't say anything about yet), I should have some more time to play and find new things out. I've been experimenting with font embedding and custom characters, and that should be a pretty cool post coming soon!

0 Comments
Filed under: ,

I was playing around with Blend today, and decided that I wanted to write a function to allow my user to drag the shapes around on my artboard. Granted, this could use a bit of polish, since there are some tweaks that would make it a bit better, but the overall sense should work.

For starters, we need to add a couple private variables to our Window... so I added these three lines:

private Point initialDragPoint = new Point(0, 0);

private Point currentDragPoint = new Point(0, 0);

private bool dragging = false;

My naming may not be the best, but what I'm setting up here is to keep track of where I clicked the mouse on my object, and for any mouse event, where I am now. I'm also setting up a boolean flag, just to make the code a bit cleaner.

Now, I've already opened Blend, and created a Rectangle. Also, since I don't really want/need all of the layout power of the Grid, I've changed the layout type of my root element to a Canvas. You COULD do this whole thing with RenderTransforms, but in this case, I think that's just more hassle then it's worth.

We need to hook up three events... MouseDown, MouseMove and MouseUp. We'll start with MouseDown...

private void StartMove(object sender, System.Windows.Input.MouseButtonEventArgs e)

{

      if (!this.dragging)

      {

            this.initialDragPoint = e.GetPosition(this);

            this.dragging = true;

      }

}

Ok, this method won't do a whole heck of a lot, but what it does is remember the point where the mouse came down, then swap the flag to remember that we're now moving. You'll want to hook this event up to the "MouseDown" event on your shape(s) that you want to allow the user to move.

Next, we'll do MouseUp (which is when the user releases the mouse button).

private void EndMove(object sender, System.Windows.Input.MouseEventArgs e)

{

      if (this.dragging)

      {

            this.dragging = false;

      }

}

Again, another simple one... all we do is tell our system that we're done dragging when the user lets go of the mouse button. Finally, the workhorse... MouseMove.

private void Moving(object sender, System.Windows.Input.MouseEventArgs e)

{

      if (this.dragging)

      {

            this.currentDragPoint = e.GetPosition(this);

            Vector dragVector = this.currentDragPoint - this.initialDragPoint;

            FrameworkElement actor = sender as FrameworkElement;

            double curLeft = (double)actor.GetValue(Canvas.LeftProperty);

            double curTop = (double)actor.GetValue(Canvas.TopProperty);

            curLeft += dragVector.X;

            curTop += dragVector.Y;

            actor.SetValue(Canvas.LeftProperty, curLeft);

            actor.SetValue(Canvas.TopProperty, curTop);

            this.initialDragPoint = this.currentDragPoint;

      }

}

Let's look at it piece by piece.

First, obviously, we only want to do something if we're currently dragging. So, we gate the whole method to only act if this.dragging is true.

Next, we figure out where the mouse currently is. e.GetPosition(this) does that for me. For the record, the 'this' as an argument just says that I want the point relative to the window. Since I used (this) in MouseDown as well, both of my points will be in the same scale.

To see how this works, let's say that when we did the MouseDown, we were 100 pixels down and 100 pixels to the right of the top left of our window. Then, when we got the MouseUp, we were 120 pixels down and 120 pixels to the right. So, this.initialDragPoint would be (100,100). The e.GetPosition(this) sets the currentDragPoint to (120,120).

The next step is to calculate our dragging Vector. We want our rectangle to move down and to the right, so the calculation is easy... Now, I could have used a Point just as easily, but I tend to think of this as an actual movement vector, so I used the Vector class instead. dragVector gets set to (120-100, 120-100) or (20,20), which is the movement we want on our rectangle.

Next, I need to get the actor (or the shape that my user is moving), and I need it as a FrameworkElement. By casting it to a Framework element, this same code would work for anything... a Grid with a bunch of elements in it... a single rectangle... pretty much anything.

Then, I just use GetValue to get the current Left and Top (see why I wanted to use a Canvas), adjust them by the dragVector, and then set those values.

Finally, the last thing I need to do, is set THIS point as my new base point, so that the new drag is relative to here. Imagine if I hadn't reset it, and then moved the mouse 20 more pixels to the right. The initial point would be 100,100, but the new dragPoint would be (140,120), so my rectangle, which I already moved 20x20 pixels, would be moved another 40x20 pixels. That's no good. So, updating that base value is important.

Granted, there are still some quirks here... for instance, you could start the drag with the left mouse button, then press the right mouse button while dragging... then releasing EITHER button would drop it. I'm sure there are plenty of refinements... but the point is that this should give everyone a good starting point.

2 Comments
Filed under: ,

Well, it's official, I'm going to be presenting at the MIX convention!

I'm really excited for this, although it's going to be busy for a little while. We need to do dry runs and such. The tentative title is "Unlocking the Creative Genome", but that title may change.

I don't know how many folks who read my blog will be at MIX, but if you are, please stop by and introduce yourself. I'm also going to be in the Expression Blend sandbox, helping folks out with labs and such. I'll send more information as I get it.

If you're not familiar with the MIX, check out http://www.visitmix.com.

CORRECTION: Due to technical issues, my trip to the MIX has been cancelled. My apologies to anyone who was planning on coming to see me there. :(

1 Comments
Filed under:

Here's the problem. Launch Blend, and create a window that has AllowsTransparency set to true (which sets the WindowStyle to None). Looks good, yes? Now, add a button, and in the Click handler, set the WindowState property to Minimized.

Run the application on Vista, and click your minimize button. You'll notice that the window just vanishes! It doesn't get the nice minimize behavior like you'd expect (You know, the animation. Minimize any other app, and you'll see what I mean).

One of my teammates, Brian, was trying to get this to work, and he did some research. Apparently, if you set this.WindowStyle to WindowStyle.SingleBorderWindow, it will minimize correctly. He was pretty excited about this, and brought it to me. I'm not completely sure why it works, because when you restore the window, it's still the "none" WindowStyle, but it does work.

Problem is, if you run the minimize behavior a second time, it fails with an InvalidOperationException, and your app comes crashing down.

We tried resetting the WindowStyle back to None after Minimizing it, but that caused a different InvalidOp.

So, we played with it a bit, and it seems (oddly enough) that if you set the property ONCE after the window has been created, that you get the minimizing behavior. You just can't set it twice.

So, try this method:

private void MinimizeIt(object sender, RoutedEventArgs e)

{

      if (this.WindowStyle != WindowStyle.SingleBorderWindow)

      {

            this.WindowStyle = WindowStyle.SingleBorderWindow;

      }

      this.WindowState = WindowState.Minimized;

}

And from our experimentation, this would consistently cause the window to minimize in the "Vista" way, didn't cause a crash, and oddly enough, didn't change the appearance at all. Oddly enough, for grins, I tried putting that line in the Window1.Loaded event handler (Well, without the If statement), and it gives me the InvalidOperation exception.

A teammate came to talk to me, because he's working with a semi-transparent window, with no Border, and he wants to be able to resize it. Allowing a user to move, minimize, maximize and close it are easy, but how to resize?

Well, this is actually pretty easy. There is a property on the Window named ResizeMode, and if you set it to "CanResizeWithGrip", then a ResizeGrip control is placed on your window automatically, and if you run the app, it will, correctly, resize your window.

But, the default grip is six small squares arranged in a SE-pointing triangle. And, if you look in Blend, that control isn't actually in the XAML, so there's no way to modify it. You can't even make it transparent to put a customized control 'under' it. And it's magical, in that it you can't possibly get another control on top of it, even with Bring To Front... So, how do you customize the look and feel of that control?

You'll find that you can create a new ResizeGrip control, and place it in the bottom right, but you will find that it will be rendered under this mystical ResizeGrip, so no customization there either...

Under the covers, here's the problem. That ResizeGrip is not 'technically' part of the Window. Just as WPF is about to instantiate the window, it sees that the ResizeMode is set to CanResizeWithGrip, and it creates that grip for you in the SE corner. It simply instantiates the control, sets the properties to show up in the bottom left, and that's it. Unfortunately, this means that you can't modify any of the properties on it, since it's actually created when the Window is Instantiated.

I suppose, if you wanted to, you could, in code, track down that control by looking at the children of the Window, although I'm not even sure it *IS* technically even there.

You could also create your own custom control (probably inherited from Thumb), and implement all your own MouseDown/Up/Move code to do your resizing for you. 

Thankfully, here's a better solution. Drop a ResizeGrip somewhere on your document, and edit a copy of the template. But, when the dialog comes up to let you know that you're creating a new Template, click the "Apply To All" radio button, instead of supplying a name. By specifying Apply To All, you're telling WPF that any new controls of this type, unless they specify a new template, should use the template that you're creating at this moment. Go ahead and trick out the ResizeGrip however you want in the Template, then delete the ResizeGrip that you created at the beginning of this process.

Now, you've got a new default ResizeGrip template, so now when WPF instantiates that ResizeGrip, it gets your new default template, and gets the look and feel you want.

The obvious question, now, is that once you've done this, what if you want different ResizeGrips for different areas of your window? I.e. maybe my dialogs that show names have a resize grip with a yellow smiley face, but my dialogs for error messages show a little horned devil.

Two points here... First, when you define your new default template, you can specify the scope. So, if you scope your new template to the Application, then ALL your ResizeGrips will look identical. However, if you scope it to individual windows, those windows can have different templates.

Second, there's nothing stopping you from creating a template whose appearance is a transparent block. Then, you can simply put whatever you want your grip to look like at the bottom right, and the real resize grip will be rendered on top, but since it's transparent, you won't see it... you'll just get the functionality. Of course, if you're going to try this route, make sure your little smiley faces or whatever are laid out in such a way that they'll stay at the bottom right :).

Enjoy!

PS: If folks don't know how to set up moving/close/minimize behavior on transparent windows, give me a shout, and I'll put something here for those scenarios.

0 Comments
Filed under:

The honorable Major Nelson just posted that someone created a Vista Gadget that will display the GamerTag information for your friends on XBox Live. Well, for those who don't know, if I'm not mistaken, Vista Gadgets are generally written in Silverlight (Please correct me if someone knows better).

But, how to get that information? Well, Silverlight doesn't have Databinding yet, but WPF does. If you check out the Major's post, he points you to a Microsoft Evangelist by the name of Adam Kinney who wrote the gadget. Following him, you end up with Duncan Mackenzie who talks about how to connect to his services and get GamerTag info.

Here, to make things easier, open up Blend, and try this:

Create a new project, and then click +XML in the Data Pane. The dialog will come up asking for DataSource info. Paste this into the URL for the XML Data:

http://duncanmackenzie.net/services/GetXboxInfo.aspx?GamerTag=LordSaphon

Now, check out the Data pane. You'll see XboxInfoDS, which you can expand to see a bunch of data fields. If you drag out the "Gamertag (string)" onto the design surface and create a Label, it will proudly display "LordSaphon" (since that was the Gamertag you supplied in the URL, and yes, I am LordSaphon).

You can go ahead and drag the GamerScore, ReputationImageUrl and so on out to the artboard and you'll see those fields displayed as well. Please, don't laugh at my GamerScore... I know it's not huge :).

Remember that any field that is an image Url should probably be bound to an Image control. You can create your own WPF Gamertag application. It also shouldn't be too difficult to modify the Source property of the DataSource, so you can dynamically switch which user you want to display!

Enjoy!

--Dante (LordSaphon on XBox Live)

PS: The service I'm using for getting the XBox Live information is on a webpage not affiliated with Microsoft (as far as I know), so if that information changes, the results you get are beyond my control. This information is provided "as-is", so don't hold me or Microsoft responsible if you get anything unexpected from the site.

References:

Duncan Mackenzie: http://duncanmackenzie.net/blog/put-up-a-rest-api-for-xbox-gamertag-data/default.aspx

Adam Kinney: http://adamkinney.com/blog/272/default.aspx

Major Nelson: http://majornelson.com/archive/2007/10/25/xbox-friends-watch-a-sidebar-gadget-using-silverlight.aspx

I just worked through figuring this out today, and since it was sufficiently irritating, I thought I'd post it here. Plus, I don't want all my 'loyal readers' to think I've disappeared :).

Anyway, in the application I'm working on, I have a ListBox with specific elements in it. If it were databound, I know how to change the selection color, but in this case, I couldn't seem to figure out how to get anything except the System Colors.

If you want to see what I mean in Blend, drop a ListBox on the artboard and using the Right Click menu, add a couple ListBoxItem to it. Now, run the app. When you select any of the items, it is highlighted in whatever color your computer has as the SelectedItem color (On my machine, it's Silver, because I'm using XP with a Silver color scheme).

However, digging through the properties of the ListBox, I couldn't find which property was bound to that System Resource. So, how could I override it and change it.

Here are the steps I eventually had to do: (These are the steps from the start)

  1. Create a ListBox
  2. Add a ListBoxItem to the ListBox
  3. Select the ListBoxItem
  4. Edit the Template of the ListBoxItem (Remember the name, you'll need it later)
  5. Now, in the Triggers pane, choose the property trigger for "IsSelected = True"
  6. Select the Bd element
  7. In the Property pane, you will see that next to the Background brush, the property marker (the little square) is green. This is indicating that it's using a resource. Just change the brush type to something else, and the marker will switch back to white. You can now edit the background to whatever you want.

Now, with the IsSelected Property Trigger selected, any changes you make will be put into the Template, and you're good to go. You will probably want to go back to the root (using the scope up button), and assign that Style to all the ListBoxItem, so select them all in the Object Tree, and set the Style property to the local resource that was created in step 4 above.

1 Comments
Filed under:

It may not be a huge revelation for other folks, but with some of the testing I've been doing, I really started getting frustrated with some stuff.

Specifically, I've got a fairly complex animation, and I want it to repeat forever. However, setting the repeat count on each and every property that I'm animating is rather tedious. "Set the repeat count on the X Rotation... set the repeat count on the Y Rotation... set the repeat count on the X Skew... *sigh*... Set the repeat count on the Y Skew..." etc...

Now, you can't set the repeat count on a composite keyframe in the hopes that the RepeatCount will be propogated downward inside Blend (Yes, I've asked my PMs for that feature, and MAYBE it will make it into the product some day, but who knows...).

So, how do you handle this? Now, I'm sure the XAML experts already knew this, but if you go into the XAML, you can just add RepeatBehavior="Forever" to the Storyboard. The XAML now looks like:

<Window.Resources>
  <Storyboard x:Key="Timeline1" RepeatBehavior="Forever">

And that's it. Now, the animation will start at the beginning, run until the time when all the existing animations have ended, then start over again.

Again, most of the XAML folks probably already knew this, but I thought it was pretty cool.

0 Comments
Filed under:
I've been asked how to create a Value Converter that you can configure the behavior of. In my Total Training series I showed you how to create a Value Converter that would return a color, Red if the value was below a hard coded value, Yellow if it's between the Red threshold and another value, and Green otherwise.

But, in that case, the Red, Yellow and Green thresholds were hard coded into the VC. What if I want a more generic Value Converter that I can specify the thresholds, and even further, what if I want to specify all the colors myself. (I.e. if the value is 100, turn Blue or something).

This can be done in WPF by creating a parameterized Value Converter, and Blend does support working with those kinds of VCs, so let's look at how to do it.

First, create the simple "Rectangle, TextBlock, Slider" combination. We're going to bring the Slider to the text of the TextBlock, and change the Fill of the Rectangle based on the value.

Here's some XAML if you want to just use what I've got:

<Grid Margin="77,60,255,0" VerticalAlignment="Top" Height="34">

     <Rectangle HorizontalAlignment="Left" Margin="0,0,0,6" Width="49" Fill="#FFFFFFFF" Stroke="#FF000000" RadiusX="7.5" RadiusY="7.5"/>

     <TextBlock HorizontalAlignment="Left" Margin="53,6,0,0" Width="19" Text="{Binding Path=Value, ElementName=Slider, Mode=OneWay}" TextWrapping="Wrap"/>

     <Slider Margin="76,0,0,6" x:Name="Slider" LargeChange="10" Maximum="100" SmallChange="1" IsSnapToTickEnabled="True"/>

</Grid>

 

Ok, so now let's create the basic three-tier Value Converter. For now, we're going to say a value of 80 or higher is Green, 50 or higher is Yellow and anything else is Red. Here's the code:

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Data;

using System.Windows.Media;

 

namespace ValueConverters

{

    class ThreeTierHardCodedVC: IValueConverter

    {

        #region IValueConverter Members

 

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            try

            {

                double inputValue = System.Convert.ToDouble(value);

                if (inputValue >= 80)

                {

                    return new SolidColorBrush(Colors.Green);

                }

                else if (inputValue >= 50)

                {

                    return new SolidColorBrush(Colors.Yellow);

                }

                else

                {

                    return new SolidColorBrush(Colors.Red);

                }

            }

            catch (Exception e)

            {

                return new SolidColorBrush(Colors.White);

            }

        }

 

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        #endregion

    }

}

 

Ok, so far so good. If you databind the fill of the Rectangle to the Text of the TextBlock, and pass it through this VC, you'll see that the Rectangle changes color as you move the slider. So, the next step is how we're going to parameterize the VC. For now, I'm just going to accept a two element string, seperated by semicolons. I use semicolons instead of commas just so I don't need to worry about internationalization right now.

So, in effect, if the user passes 80;50, I'm stating that when the value is greater then 80, turn green, between 80 and 50, turn yellow, and below 50, turn red. The parameter will be passed to us (unsurprisingly) as the parameter named "parameter". We'll take it, split it into an array, and just parse those values out. To see how this works, try hooking up one slider like this using 80;50 and another at 95;5.

This is better, except that you're forced into the red/yellow/green colors. What if you want to expand on that? What if you want to be able to configure those colors yourself. To do this, I wrote a quick function that takes a string and converts it to a color. It's not too difficult to write, but if you want to use it, here it is:

private Color GetColorFromString(string input)

{

      Type colorClass = typeof(Colors);

      List<MethodInfo> colorMethods = new List<MethodInfo>(colorClass.GetMethods());

      foreach (MethodInfo info in colorMethods)

      {

            string methodName = info.Name;

            if (info.Name.StartsWith("get_"))

            {

                  string actualColor = info.Name.Split('_')[1];

                  if (String.Compare(input, actualColor, true) == 0)

                  {

                        // We have a match!

                        return (Color)info.Invoke(null, null);

                  }

            }

      }

      return Colors.Black;

}

This method will take any color that is in the Colors object, and return the appropriate color. (If someone knows an easier way to do this, I'd love to hear it). You will need to add System.Windows.Media, System.Windows.Data and System.Reflection to use my method. If you want more info on the Colors class, take a look at: http://msdn2.microsoft.com/en-us/library/system.windows.media.colors.aspx

Ok, so now with this, we're going to modify our VC one more time, to allow it to take three arguments. My plan is to go through the arguments one at a time, check to see if our input is greater then the first, if so, return the color, if not, go on to the second. I'm actually going to ignore the first piece of the final argument, since it will simply end up as the default. I'm going to seperate the arguments with ';', and individual pieces with a ':'. So, here's the new code:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{

      try

      {

            string paramAsString = parameter.ToString();

            string[] splitParam = paramAsString.Split(';');

            string[] firstArgument = splitParam[0].Split(':');

            string[] secondArgument = splitParam[1].Split(':');

            string[] thirdArgument = splitParam[2].Split(':');

            double firstInterval = System.Convert.ToDouble(firstArgument[0]);

            Color firstColor = this.GetColorFromString(firstArgument[1]);

            double secondInterval = System.Convert.ToDouble(secondArgument[0]);

            Color secondColor = this.GetColorFromString(secondArgument[1]);

            Color thirdColor = this.GetColorFromString(thirdArgument[1]);

            double inputValue = System.Convert.ToDouble(value);

            if (inputValue >= firstInterval)

            {

                  return new SolidColorBrush(firstColor);

            }

            else if (inputValue >= secondInterval)

            {

                  return new SolidColorBrush(secondColor);

            }

            else

            {

                  return new SolidColorBrush(thirdColor);

            }

      }

      catch (Exception e)

      {

            return new SolidColorBrush(Colors.White);

      }

}

If you give THIS one a try, set the parameter to something like: 95:Azure;10:Coral;0:SeaGreen. Now we've got a VC that you can configure all three brackets, as well as the colors that go with them.

Of course, this is hideously inelegant, so I want to make one last change. Instead of hard coding and breaking the parameters apart this way, I'm going to just create a loop. This will tighten up the code, and actually allow us to go an arbitrary number of brackets. So, if you need to have 15 different brackets of different colors from Indigo to MintCream, you can do it. So, we'll pull those ugly "secondParameter" lines out, and replace it with a foreach. (I've included the entire class here, in case you want to cut and paste)

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Data;

using System.Windows.Media;

using System.Reflection;

 

namespace ValueConverters

{

      class FullyVariableVC : IValueConverter

      {

            private Color GetColorFromString(string input)

            {

                  Type colorClass = typeof(Colors);

                  List<MethodInfo> colorMethods = new List<MethodInfo>(colorClass.GetMethods());

                  foreach (MethodInfo info in colorMethods)

                  {

                        string methodName = info.Name;

                        if (info.Name.StartsWith("get_"))

                        {

                              string actualColor = info.Name.Split('_')[1];

                              if (String.Compare(input, actualColor, true) == 0)

                              {

                                    // We have a match!

                                    return (Color)info.Invoke(null, null);

                              }

                        }

                  }

                  return Colors.Black;

            }

 

            #region IValueConverter Members

            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

            {

                  try

                  {

                        string paramAsString = parameter.ToString();

                        string[] splitParam = paramAsString.Split(';');

                        double inputValue = System.Convert.ToDouble(value);

                        foreach (string bracket in splitParam)

                        {

                              string[] bracketParameters = bracket.Split(':');

                              Double bracketLimit;

                              try

                              {

                                    bracketLimit = System.Convert.ToDouble(bracketParameters[0]);

                              }

                              catch (InvalidCastException)

                              {

                                    return this.GetColorFromString(bracketParameters[1]);

                              }

                              if (inputValue >= bracketLimit)

                              {

                                    return new SolidColorBrush(this.GetColorFromString(bracketParameters[1]));

                              }

                        }

                  }

                  catch (Exception e)

                  {

                        return new SolidColorBrush(Colors.White);

                  }

                  return new SolidColorBrush(Colors.White);

            }

 

            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

            {

                  throw new Exception("The method or operation is not implemented.");

            }

            #endregion

      }

}

 

Give this one a fly, and set the parameter to: 95:Azure;75:HotPink;40:Blue;10:Coral;-9999:Orange.

Enjoy :).

(As always, code here is provided "As-is" with no warranty, implied or otherwise. Use at your own risk)

Is when I can't talk about something!

By now, most folks should be downloading the August preview of Expression Blend 2, which I'm super excited to see finally getting out. (If you didn't already know... Get it HERE)

But, one of the features that I've been testing personally is one I'm really excited about, but got cut from the August preview for a variety of reasons. For obvious reasons, I can't say what the feature was and why it was cut. And that drives me CRAZY sometimes!

I can't wait to start talking about the new feature, what you can do with it, how much fun I've had playing with it and so on... and now I have to wait until our next tech preview. And even *I* don't know how long that's going to be. (No, it's not a case of "I know but can't say in a public forum", but I actually don't know).

Ah well, I guess my loyal readers will just have to wait and see.

And, for the record, I should have a new blog post coming out soon that describes how to do a parameterized Value Converter. In the Total Training series (Check out http://totaltraining.com/microsoft for details), I go over how to create a Value Converter that changes a control different colors based on the value (I.e. if it's greater then 90, turn it green, etc...). However, I had someone ask me privately how to create a generic one that you could configure. I've got a couple solutions, and I'm putting the polish on those now.

0 Comments
Filed under: ,

Ok, see this is what happens when I have some time to 'play' with Blend.

My previous post showed how to modify the stylus tip to get something akin to a calligraphy pen. But, the pen that I got most recently got is called a scroll point. The tip itself looks like a fairly broad pen, but there's a 'gap' in the middle, so it's effectively drawing two parallel lines. You use the same gestures you'd use with a normal pen, it just looks somewhat different. (Sorry, I can't seem to find a bloody link on the web to show you what I'm talking about).

So, I want to duplicate THIS effect. It's a bit trickier then it was with the widening the tip, but it's still doable.

First, I need to implement my own handler for when I'm working with the Stylus, so I can intercept the event and add the second (and for fun) third line. I only want to draw when the stylus is down, so easily enough, add a property on the paper for when we're drawing. (Now, here I was a bit lazy and did it on the Window itself, but moving it the paper should be easy enough).

public partial class Window1

{

      bool drawing = false;

 

      public Window1()

      {

            this.InitializeComponent();

      }

 

      private void OnStylusDown(object sender, System.Windows.Input.StylusDownEventArgs e)

      {

            this.drawing = true;

      }

 

      private void OnStylusUp(object sender, System.Windows.Input.StylusEventArgs e)

      {

            this.drawing = false;</