Snoop Now Works in WPF Interop Scenarios, Part II

First off, in my previous post, I made the mistake of assuming (given the new look and new functionality) that Pete Blois had bumped the version of Snoop up to 2.0. This does not seem to be the case. So, oops! My apologies!

Now, onto the meat of post: I have found a couple more places where you need to insulate yourself from not having a current Application … and I was able to fix the keyboard issue!

Let’s tackle the keyboard issue first as that is the big one. In the Inspect method of the SnoopUI class, you need to call the static method ElementHost.EnableModelessKeyboardInterop and you need to do that when in an interop scenario, that is, when the current Application is null.

The MSDN documentation for this method states that this method allows a System.Windows.Window to receive keyboard messages correctly when opened modelessly from Windows Forms (our interop scenario). Hmm. Sounds exactly like what I was looking for!

public void Inspect(object target)
{
    this.rootObject = target;
    this.Load(target);
    this.CurrentSelection = this.root;

    this.OnPropertyChanged("Root");

    if (Application.Current != null)
    {
        this.Owner = Application.Current.MainWindow;
    }
    else
    {
        ElementHost.EnableModelessKeyboardInterop(this);
    }
    this.Show();
}


This also requires you to add a reference to WindowsFormsIntegration.dll and to add a using statement:

using System.Windows.Forms.Integration;

With the above changes, you should be able to use the keyboard once again to change property values in the property grid that comes up when you Snoop something. Whew! That copy/paste trick was getting old, really fast.

Now, onto the additional places where you need to protect yourself from not having an Application instance. First, the Previewer was broken … and you need to modify the HandleMagnify method of the Previewer class:

private void HandleMagnify(object sender, ExecutedRoutedEventArgs e)
{
    Zoomer zoomer = new Zoomer();
    zoomer.Target = this.Target;
    if (Application.Current != null)
        zoomer.Owner = Application.Current.MainWindow;
    zoomer.Show();
    e.Handled = true;
}


Second, you need to modify the ProcessFilter method of the SnoopUI class so that it uses the Dispatcher for the current thread versus grabbing it off the non-existent Application instance:

private void ProcessFilter()
{
    this.filtered.Clear();

    // Blech.
    if (this.filter == "Clear Filter")
    {
        Dispatcher dispatcher = null;
        if (Application.Current == null)
            dispatcher = Dispatcher.CurrentDispatcher;
        else
            dispatcher = Application.Current.Dispatcher;

        dispatcher.BeginInvoke
        (
            DispatcherPriority.Loaded,
            new DispatcherOperationCallback
            (
                delegate(object arg)
                {
                    this.Filter = string.Empty;
                    return null;
                }
            ),
            null
        );
        return;
    }
    if (this.filter == "Visuals with binding errors")
        this.FilterBindings(this.root);
    else if (this.filter.Length == 0)
        this.filtered.Add(this.root);
    else
        this.FilterTree(this.root, this.filter.ToLower());
}

By the way, I would love to hear (by leaving a comment on the blog here), if this helps you at all. I was truly missing the ability to Snoop our WPF interop application myself.

Enjoy!

p.s.

I would love to make my source code modifications to Snoop readily available to the general public, but I have been unable to contact Pete Blois and obtain his permission to do so. Sorry!

(The source code and binaries for Snoop are now (see posts: 1, 2) up on CodePlex. Please go download it from there as that code will contain the latest and greatest bits.)

Share

Snoop Now Works in WPF Interop Scenarios, Part I

Background:

Coming from the land of Windows Forms, I have grown addicted to having tools like wfSpy and Hawkeye. A software engineer at the presentation layer needs to have tools like these in order to inspect the objects in a running application and change properties on the fly.

Very soon after jumping in head first into WPF, I discovered Pete Blois’ utility: Snoop. It was definitely what I was looking for. Unfortunately, after playing with it for a while, I learned that it didn’t support our WPF interop scenario. In particular, our application is a Windows Forms application that is hosting WPF content through ElementHost. When you use ElementHost, you don’t have an Application object and Snoop coughs up a hairball because of that.

After that, Josh Smith created Woodstock and Mole soon followed. These are both debugger visualizers, and in particular, Mole is extremely nice and powerful. The Achilles tendon of debugger visualizers, however, is that you have to be running in the Visual Studio debugger. There are many cases where you can’t or don’t want to do this.

Josh Smith realized this and has created Crack.NET. This is a heap walker in the vein of Snoop and Mole … but it can be run outside of the debugger. It is a useful tool in its own right, and of particular note, you can modify the object you’ve ‘cracked’ using IronPython in the tool’s Scriptorium area.

However, even given the above, I was still missing the functionality of good, old wfSpy. Snoop was very close to what I wanted, it just didn’t handle the WPF interop story.

So, after reading this post in the WPF forums, I finally broke down, dove into the source code and figured out why. It was rather easy to fix, actually, and here is what I had to do.

 

The Fix:

In the Enqueue method of the DelayedCall class, you need to get hold of the Dispatcher from something other than the Application.Current object. Here is the whole Enqueue method:

public void Enqueue()
{
    if (!this.queued)
    {
        this.queued = true;

        Dispatcher dispatcher = null;
        if (Application.Current == null)
            dispatcher = Dispatcher.CurrentDispatcher;
        else
            dispatcher = Application.Current.Dispatcher;

        dispatcher.BeginInvoke
        (
            this.priority,
            new DispatcherOperationCallback(this.Process),
            null
        );
    }
}


In the Inspect method of the SnoopUI class, you once again need to protect yourself from not having an Application object:

public void Inspect(object target)
{
    this.rootObject = target;
    this.Load(target);
    this.CurrentSelection = this.root;

    this.OnPropertyChanged("Root");

    if (Application.Current != null)
        this.Owner = Application.Current.MainWindow;
    this.Show();
}

After those two changes, you can Snoop WPF content hosted in a Windows Forms application through ElementHost. Do I hear a ‘hallelujah’?

On a related note:

One thing I should mention is that property editing is broken in Snoop 2.0. In the WPF forum post mentioned above, Mark points out how to fix this. You just need to add the following line in the correct place in the PropertyInformation constructor:

binding.Mode =
	property.IsReadOnly ?
	BindingMode.OneWay :
	BindingMode.TwoWay;

However, this doesn’t seem to fix it completely when you are running in a WPF interop scenario … because there is still an issue to find and fix. Namely, you can’t use the keyboard to modify the property values. However, I have discovered that you can paste a value in using the clipboard. If you find out how to fix this, please let me know.

Finally:

Mark also has fixes for a couple other items. One takes care of an issue when the nesting gets too deep and another is a fix for popping the context after you ‘delve’ in on something.

I hope this helps someone. I personally am happy to the point of tears that I will now be able to Snoop our application!

Update:

I have found the keyboard issue and have fixed it. I have also uncovered a couple other changes that you need to make. For example, the Previewer wasn’t working. See my next blog post for details.

Update:

(The source code and binaries for Snoop are now (see posts: 1, 2) up on CodePlex. Please go download it from there as that code will contain the latest and greatest bits.)

Share

GPU Effects & Blurry Text

About a month ago, I ran into a weird issue … that literally confounded me … and irked me to no end. The issue was that whenever I applied a drop shadow effect to a border that contained text … the text would blur a little bit.

So, if I did the following:

<Window
    x:Class="EffectsAndBlurryText.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Effects and Blurry Text (Good)"
    Height="300"
    Width="500"
>
    <Grid>
        <Border
            Margin="20"
            Background="White"
            BorderBrush="DarkBlue"
            BorderThickness="3"
            CornerRadius="25"
        >
            <TextBlock
                Text="This text is not blurry."
                FontSize="24"
                TextWrapping="Wrap"
                Foreground="DarkBlue"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
            />
        </Border>
    </Grid>
</Window>

I would get nice looking text:

ClearText

 

But if I added the drop shadow effect:

<Window
    x:Class="EffectsAndBlurryText.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Effects and Blurry Text (Bad)"
    Height="300"
    Width="500"
>
    <Grid>
        <Border
            Margin="20"
            Background="White"
            BorderBrush="DarkBlue"
            BorderThickness="3"
            CornerRadius="25"
        >
            <Border.Effect>
                <DropShadowEffect Color="DarkGray"/>
            </Border.Effect>
            <TextBlock
                Text="Why, oh, why is this text blurry?"
                FontSize="24"
                TextWrapping="Wrap"
                Foreground="DarkBlue"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
            />
        </Border>
    </Grid>
</Window>

I would get blurry text (click on the image to better see the issue):

BlurryText

 

So, how can we get around this annoying issue? Here is what I came up with: wrap the Border in a Grid and put another Border underneath the one you had originally. Put the BorderBrush and Background on the new Border and apply the drop shadow effect there instead.

Here is the xaml:

<Window
    x:Class="EffectsAndBlurryText.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Effects and Blurry Text (Fixed)"
    Height="300"
    Width="500"
>
    <Grid>
        <Border
            Margin="20"
            BorderThickness="3"
            CornerRadius="25"
            Background="White"
            BorderBrush="DarkBlue"
        >
            <Border.Effect>
                <DropShadowEffect Color="DarkGray"/>
            </Border.Effect>
        </Border>
        <Border
            Margin="20"
            BorderThickness="3"
            CornerRadius="25"
        >
            <TextBlock
                Text="Ah, now, the text is not blurry."
                FontSize="24"
                TextWrapping="Wrap"
                Foreground="DarkBlue"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
            />
        </Border>
    </Grid>
</Window>

 

I would love to hear if anyone has any better ideas to solve this … or just if others have noticed it as well.

I hope this helps someone … I know at least one other person has run into this. See his January 28th blog entry.

 

Note: this issue seems intermittent at times. In particular, sometimes you can see it only in the designer (both Visual Studio and Blend). However, that being said, it is not just in the designer … I have seen it in running executables.

Update 1: Others seem to have run into this issue as well. There is a current forum post about this, in fact.

Update 2: Initially, I thought this was an NVIDIA driver issue, but others have seen it on ATI cards and Intel integrated solutions. Walt Ritscher says its a known issue. I wonder if it is related to another issue that I’ve had.

Share

Kaxaml: Don’t Leave Home Without It

KaxamlLogo

Ok, I just have to give a shout out to one of the best tools out there for authoring loose xaml. And, if the logo didn’t give it away … it’s Robby Ingebretsen’s Kaxaml tool. It is available here for download … free of charge!

It’s been out for quite some time … so the purpose of this blog post is really to just publicly thank him for making such an awesome tool … one, in fact, that I don’t leave home without.

Recently, I have been composing Haikus … just because a tool like this … deserves poetry … and poetry better than mine!

But, in case you haven’t heard about it or haven’t played with it much … here is some quick info. I believe you pronounce it <ka-zam-al> … and I like to think like Shazam! Pow! (I have not confirmed this was the intent of its author … it might just be the meanderings of this crazy mind of mine.)

It has a pretty sweet list of features:

  • A snippet library with the ability to insert snippets via shortcut
  • IntelliSense
  • A folding editor
  • An integrated color picker
  • A xaml scrubber (or pretty printer or formatter or whatever you want to call it)
  • The ability to zoom in and out on your rendered xaml
  • The ability to make your xaml larger (use ctrl-wheel)
  • Handles both WPF and Silverlight
  • And more!
    Bonus: Robby recently recorded several videos that will bring you up to Kaxaml power user status in a matter of minutes. Check them out.
Share

Dr. WPF & Namescopes

Ok, let me first say that Dr. WPF is my hero. Not just because of his WPF snippet library. But especially because of his community involvement in the WPF forum.

Today, I was wracking my brain against a problem that I’ve never run into before and … and it was Dr. WPF to the rescue (via the forum).

The Problem:

I have a custom control that I was retemplating with a fancy, schmancy template. This ControlTemplate had a Resources section where I had a couple Storyboards which were targeting items in the visual tree of the fancy template. I was also launching this Storyboard from the code for the custom control.

However for some reason, it couldn’t resolve the Storyboard.TargetName(s). It kept coming back with the error: ‘<Storyboard.TargetName>’ name cannot be found in the name scope of ‘<Namespace.CustomControl>’.

(This is where the hair pulling happened.)

The Solution:

After some research: here, here, and here … I stumbled across Dr. WPF’s forum answer on the matter. That led me to look very closely at how I was calling the Begin on the Storyboard.

Here is the old and bad code:

And, here is the good code:

What did I do differently? Well, I had always thought that the argument to the Begin method above was an object containing the items that are being targeted by the Storyboard … and that is what the IntelliSense parameter tends to suggest (containingObject) …

However, looking closer at the documentation on the parameter it says: An object contained within the same name scope as the targets of this Storyboard’s animations. Ah, ha!

After that, all I had to do was grab one of the elements in the visual tree (_button in the above code snippet) and pass that to the Begin method (whereas before I was just passing the custom control instance in, the ‘this’ in the first code snippet above).

And all of this is due to a friendly answer by the good doctor.

Thanks Doc!

Update:

I think there is another and better way to solve this problem. Check out this question/answer on StackOverflow. Bascially, there is an overload to the Begin method that lets you send in the control template. Nice. Don’t know how I missed that overload.

Share

So Cool: GPU Based Effects in WPF

I just have to blog about this … although there must be a million other things that I would like to blog about. (Where do people find the time?)

With the service release of .NET 3.5 (now in beta), you can program effects that will run on the GPU. This means all the coolness, none of the badness (slow performance).

Greg Schechter is running a series of blog posts to introduce people on how to create these effects.

Channel 9 also recently had a great video by David Teitlebaum covering some of the new graphics capabilities (including GPU rendered effects) with the soon-to-be-release .NET 3.5 SP1.

Here are some more posts on .NET 3.5 SP1:

I can’t wait to see the effects that are going to come out of the WPF community.

Very cool news indeed.

Cory

Share