Best wpf questions in June 2011

.NET Memory issues loading ~40 images, memory not reclaimed, potentially due to LOH fragmentation

15 votes

Well, this is my first foray into memory profiling a .NET app (CPU tuning I have done) and I am hitting a bit of a wall here.

I have a view in my app which loads 40 images (max) per page, each running about ~3MB. The max number of pages is 10. Seeing as I don't want to keep 400 images or 1.2GB in memory at once, I set each image to null when the page is changed.

Now, at first I thought that I must just have stale references to these images. I downloaded ANTS profiler (great tool BTW) and ran a few tests. The object lifetime graph tells me that I don't have any references to these images other than the single reference in the parent class (which is by design, also confirmed by meticulously combing through my code):

enter image description here

The parent class SlideViewModelBase sticks around forever in a cache, but the MacroImage property is set to null when the page is changed. I don't see any indication that these objects should be kept around longer than expected.

I next took a look at the large object heap and memory usage in general. After looking at three pages of images I have 691.9MB of unmanaged memory allocated and 442.3MB on the LOH. System.Byte[], which comes from my System.Drawing.Bitmap to BitmapImage conversion is taking pretty much all of the LOH space. Here is my conversion code:

public static BitmapSource ToBmpSrc( this Bitmap b )
{
    var bi = new BitmapImage();
    var ms = new MemoryStream();
    bi.CacheOption = BitmapCacheOption.OnLoad;
    b.Save( ms,  ImageFormat.Bmp );
    ms.Position = 0;
    bi.BeginInit();
    ms.Seek( 0, SeekOrigin.Begin );
    bi.StreamSource = ms;
    bi.EndInit();
    return bi;
}

I am having a hard time finding where all of that unmanaged memory is going. I suspected the System.Drawing.Bitmap objects at first, but ANTS doesn't show them sticking around, and I also ran a test where I made absolutely sure that all of them were disposed and it didn't make a difference. So I haven't yet figured out where all of that unmanaged memory is coming from.

My two current theories are:

  1. LOH fragmentation. If I navigate away from the paged view and click a couple of buttons about half of the ~1.5GB is reclaimed. Still too much, but interesting nonetheless.
  2. Some weird WPF binding thing. We do use databinding to display these images and I am no expert in regards to the ins and outs of how these WPF controls work.

If anyone has any theories or profiling tips I would be extremely grateful as (of course) we are on a tight deadline and I am scrambling a bit to get this final part done and working. I think I've been spoiled by tracking down memory leaks in C++ ... who woulda' thought?

If you need more info or would like me to try something else please ask. Sorry about the wall-o-text here, I tried to keep it as concise as possible.

This blog post appears to descibe what you are seeing, and the proposed solution was to create an implementation of Stream that wraps another stream.

The Dispose method of this wrapper class needs to release the wrapped stream, so that it can be garbage collected. Once the BitmapImage is initialised with this wrapper stream, the wrapper stream can be disposed, releasing the underlying stream, and allowing the large byte array itself to be freed.

The BitmapImage keeps a reference to the source stream so it keeps the MemoryStream object alive. Unfortunately, even though MemoryStream.Dispose has been invoked, it doesn't release the byte array that the memory stream wraps. So, in this case, bitmap is referencing stream, which is referencing buffer, which may be taking up a lot of space on the large object heap. There isn't a true memory leak; when there are no more references to bitmap, all these objects will (eventually) be garbage collected. But since bitmap has already made its own private copy of the image (for rendering), it seems rather wasteful to have the now-unnecessary original copy of the bitmap still in memory.

Also, what version of .NET are you using? Prior to .NET 3.5 SP1, there was a known issue where a BitmapImage could cause a memory leak. The workaround was to call Freeze on the BitmapImage.

Gaussian blur leads to white frame around image

13 votes

I'm applying a blur effect to an image in WPF like so:

<Image ClipToBounds="True">
    <Image.Effect>
        <BlurEffect Radius="100" KernelType="Gaussian" RenderingBias="Performance" />
    </Image.Effect>
</Image>

As you can see, the radius is large, because the image is large and I need it to be really blurry. However, for a radius that large I'm getting a light frame around my image as seen in the attached image. How can I suppress this?

In case you're wondering: The result is the same not matter the RenderingBias. A border is also produced in quality-mode.

White border around image

What's happening is the result of a blur together with the ClipToBounds. Since you're using a Gaussian blur, the edges are going to naturally blend into the background (white).

Applying ClipToBounds basically cuts off where it would otherwise have been blending into the white, hence why you get a white frame.

Unless you're willing to clip the image even more, unfortunately this is just how blurs work.

Screenshot of cliptobounds

Is excessive nesting of WPF layout panels (e.g. Grid) computationally expensive?

11 votes

folks

I have heard from a coworker that I - as a designer using Microsoft Expression Blend - should avoid using excessive nesting of panel elements, because they are computationally expensive.

For example, I tend to create the mainwindow with header and custom statusbar with grid, and then take the top panel and put a grid inside it, and if I have a message inside a rectangle on the already gridded top panel I create yet another grid, etc.

As a very layout-oriented disigner (who wants to use every screen most efficiently whatever the screen dimensions are) I know this is the best way to do it considering absolute control and flexibility, which prevent the window to resize in "unpredictable" ways ;oP

BUT... ...this friend of mine said that, if you have, say, five grids nested inside one another, if you pass the mouse over them, you generate five mouse events, which is costly.

Also, if you have too many calculations due to the too many containers asking for children sizes before the actual rendering, it can also be costly.

I had some previous experience with PyGtk, and I must say I used A LOT o layout panels for all my scripts, and even the resizing of windows never seemed to me to be specially costly, except when I had some complex canvas drawing needed to be recalculated.

Does anyone have any experience or know anything about it?

Thanks a lot for reading

There's no straight-forward answer to this, but obviously the more elements you have participating in layout, the longer the measure and arrange phases are going to take for the window. Depending on which features of which Panel types you use it could be more or less costly, but for sure the more you use the more overhead there will be during the layout calculations. You can learn more about how the layout system works by reading that entire MSDN article.

In the end this is something that, unless you've gone crazy, will not often be an issue. To find out if it is causing problems for your app I suggest using the WPF Performance Suite to do some performance testing.

WPF: Evenly-sized buttons according to content of largest button

10 votes

Imagine I have two WPF buttons on a window, with content as follows:

<Button>OK</Button>
<Button>Cancel</Button>

I want these buttons to be the same width, however in a scenario where the Content is bound to a localised value for a given user's language, I don't know how wide the buttons need to be to accommodate the new content.

How might I apply a minimum width to these buttons, such that the width of the widest one (according to content) is effectively used as the MinWidth of both, thus keeping them uniform?

Or to put it another way: I don't want the buttons to be the width of their container (unless using a container in a clever way is the answer to my problem), and I don't want them each to just size to their own content because that will make them different sizes. I want something in-between. The one with the largest content to size to display it's content, and all others to size to that same width, so the widths are all equal.

I expect the answer lies in putting them in some sort of container. I know I could use a Grid and let them fill grid "cells", but the point is I don't want them to be too wide, either. I know I could have some code-behind that runs on the Content_Changed event of the buttons and sets the minwidth to that of the widest button, but I'm interested in a pure-xaml method. It might be I need to create a custom control extending ItemsControl that rusn code-behind when new items are added or re-sized and applies the width of the widest item as the MinWidth of all the other items.

Many thanks in advance.

The Grid

  <Grid HorizontalAlignment="Right" Grid.IsSharedSizeScope="true">
    <Grid.ColumnDefinitions>
        <ColumnDefinition SharedSizeGroup="A"/>
        <ColumnDefinition SharedSizeGroup="A"/>
    </Grid.ColumnDefinitions>
    <Grid.Children>
        <Button Grid.Column="0" Content="OK"/>
        <Button Grid.Column="1" Content="Cancel"/>
    </Grid.Children>
  </Grid>

This can be broken up, you just need to set the IsSharedSizeScope on a common ancestor, e.g.:

    <StackPanel Grid.IsSharedSizeScope="true">
        <Grid HorizontalAlignment="Right">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="A"/>
            </Grid.ColumnDefinitions>
            <Grid.Children>
                <Button Grid.Column="0" Content="OK"/>
            </Grid.Children>
        </Grid>
        <!-- ... -->
        <Grid HorizontalAlignment="Left">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="A"/>
            </Grid.ColumnDefinitions>
            <Grid.Children>
                <Button Grid.Column="0" Content="Cancel"/>
            </Grid.Children>
        </Grid>
    </StackPanel>

To prevent the buttons from becomming too large change the HorizontalAlignment of the Grid to something else than Stretch or set a MaxWidth.

Custom MVVM implementation Vs. PRISM

8 votes

This question is inspired from this closed question -

What does Prism actually offer the developer? And is it worth it?

I have already implemented my own custom MVVM implementations in enterprise applications. I am intrested in knowing -

why should I learn PRISM(specifically PRISM, not other MVVM frameworks)? Benefits of PRISM over custom MVVM implementation and is it worth the investment in learning PRISM?

I hope this question is not subjective and everyone pls. don't get into arguments :)

As with many frameworks that do a common task for you, you get:

  1. Tested by many more eyeballs than just yourself. This (hopefully) includes unit tests, which you may or may not be doing while building your own framework.
  2. More readable for other developers: nobody else has experience with your custom MVVM framework. But if another developer joins your project, or joins your team, or joins your company, they can jump straight into Prism code.
  3. Better documentation. Along the same lines, anyone new joining likely has to learn the ropes by manually gathering the collective knowledge from your brain, and any other users on the team, and by looking at the source code. Third party frameworks have their own documentation, and tons more blog posts on the internet.
  4. Better community. You can ask questions on StackOverflow about "how do I do X with Prism?" You can't ask that with your custom framework.
  5. Likely more capable: by needing to serve more users than just you/your team, more features will have been added. If you need to do something MVVM-related that you've never done before, chances are support for it isn't built in to your own MVVM framework. But it's likely in Prism.
  6. Better structure. Let's say you wanted to do something MVVM-related but it wasn't in Prism. Very likely, there's a good reason for that! If something's not in a (reasonably-mature) framework made for working in a given domain, that's a sign that what you're trying to do is an unnatural or awkward way of approaching the problem. Working with your own framework, it's too easy to say "oh I'll add that feature," then 6 months later realize you made a huge mistake because this new feature makes your code very hard to follow or ends up being a vector for lots of bugs or whatnot.
  7. A CV line-item: I would have mixed feelings toward hiring someone who had "implemented and used custom MVVM framework." While it could mean they're smart, it could also indicate the dreaded "not built here syndrome." On the other hand, putting "Microsoft Prism MVVM Framework" among a huge list of technologies could be nice, but isn't a wow-er. The best of both worlds would be a longer bullet point, along the lines of "Deep understanding of the MVVM pattern, achieved by first implementing a toy MVVM framework for learning purposes before switching to MVVM Prism." Yes, the difference between these three isn't going to make or break your CV, and not-built-here syndrome is something that would hopefully come up in an interview, but it's just worth keeping in mind, especially if you're applying for a place that gets enough resumes they can afford to throw out anything that unnerves them slightly.

Upside down browser in WPF application.

7 votes

I am writing a WPF application using Csharp. Its a touch application that has four browser windows displayed. Two upside down. The screen will be built into a table and lie flat. Four people will use the table - two on one side and two on the other. Thus the need for the upside down (or 180 degree rotation) browsers. I have tried using SilverLight and its Browser Brush. This works to display the browser content upside down BUT to interact with the browser you have to make the browser control active (instead of the Brush) and this turns the content back the right way up. I guess what I need is a Browser control that can be rotated in the normal way and work rotated. IE in Dot Net wont work.

Sascha Barber and Chris Cavanagh have blog posts about an alternative WebBrowser Control based on Chromium that behaves like a real WPF control. Check out http://sachabarber.net/?p=597 and http://chriscavanagh.wordpress.com/2009/08/25/a-real-wpf-webbrowser/

WPF with C++, it's possible?

7 votes

I have my main programme in C++ but now I need to build a beautiful application and I know that WPF is easy and do beautiful app so WPF can work with C++ or C# and C++? (How can I do that if it's work?) WPF is the best thing for me?

You can use WPF in a managed C++ project, but you're better off keeping your C++ in a separate assembly and referencing it from a C# or VB WPF project.

WPF Application DataGrid Control Window Switching Lag when lots of Rows in Non-Virtualized DataGrid

7 votes

We have a small .NET 4.0 DataGrid WPF demo with the code posted below. It consists of a non-virtualized DataGrid with 30 columns and 3000 rows. It is non-virtualized, due to us requiring grouping capabilities, which do not support virtualization.

When you run this app, and switch between it and other windows, there is a noticeable lag (of about 1 second). This only occurs when the window is reactivated - once activated, clicking around inside has no associated delay.

We have profiled this lag that occurs on window reactivation using the performance analyzer and have found that there are a lot of dependency property notifications being triggered when the window is brought back into focus. We do not know why this happens, and it seems unnecessary.

We find this delay is proportional to the number of rows in the DataGrid. Does anyone know how we can eliminate or reduce this lag?

UPDATE: It seems like the focus lag occurs even when staying inside the application but focusing on another control such as a textbox outside the grid. Therefore we now know it's not a Window switching problem, but one caused by change in focus, but are still unsure of the exact cause.

(MainWindow.xaml)

<Window x:Class="WpfApplication20.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid Name="dataGrid" VirtualizingStackPanel.IsVirtualizing="False" AutoGenerateColumns="True"/>
    </Grid>
</Window>

(MainWindow.xaml.cs)

using System.Collections.Generic;
using System.Windows;

namespace WpfApplication20
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            List<Row> rows = new List<Row>();

            for(int i = 0; i < 3000; i++)
            {
                Row row = new Row(i);
                rows.Add(row);
            }

            dataGrid.ItemsSource = rows;
        }
    }

    public class Row
    {
        public double Double1 { get; set; }
        public double Double2 { get; set; }
        public double Double3 { get; set; }
        public double Double4 { get; set; }
        public double Double5 { get; set; }
        public double Double6 { get; set; }
        public double Double7 { get; set; }
        public double Double8 { get; set; }
        public double Double9 { get; set; }
        public double Double10 { get; set; }
        public double Double11 { get; set; }
        public double Double12 { get; set; }
        public double Double13 { get; set; }
        public double Double14 { get; set; }
        public double Double15 { get; set; }
        public double Double16 { get; set; }
        public double Double17{ get; set; }
        public double Double18 { get; set; }
        public double Double19 { get; set; }
        public double Double20 { get; set; }
        public double Double21 { get; set; }
        public double Double22 { get; set; }
        public double Double23 { get; set; }
        public double Double24 { get; set; }
        public double Double25 { get; set; }
        public double Double26 { get; set; }
        public double Double27 { get; set; }
        public double Double28 { get; set; }
        public double Double29 { get; set; }
        public double Double30 { get; set; }

        public Row(double d)
        {
            Double1 = d;
            Double2 = d + 1;
            Double3 = d + 2;
            Double4  = d + 3;
            Double5  = d + 4;
            Double6  = d + 5;
            Double7  = d + 6;
            Double8  = d + 7;
            Double9  = d + 8;
            Double10 = d + 9;
            Double11 = d + 10;
            Double12 = d + 11;
            Double13 = d + 12;
            Double14 = d + 13;
            Double15 = d + 14;
            Double16 = d + 15;
            Double17 = d + 16;
            Double18 = d + 17;
            Double19 = d + 18;
            Double20 = d + 19;
            Double21 = d + 20;
            Double22 = d + 21;
            Double23 = d + 22;
            Double24 = d + 23;
            Double25 = d + 24;
            Double26 = d + 25;
            Double27 = d + 26;
            Double28 = d + 27;
            Double29 = d + 28;
            Double30 = d + 29;
        }             
    }                 
}

(Group Style - optionally enabled by putting inside DataGrid XML):

<!--<DataGrid.GroupStyle>
                <GroupStyle>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                        <Border BorderBrush="DarkGray" BorderThickness="1" Padding="4,0" >
                                            <Expander VerticalContentAlignment="Center" IsExpanded="True">
                                                <Expander.Header>
                                                    <Canvas>
                                                        <StackPanel Orientation="Horizontal" Canvas.Top="-11" Canvas.Left="4">
                                                            <Label Content="{Binding Name}" Visibility="{Binding DataContext.ShowGroupHeaderVisibility, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                                                            <Label Content="{Binding ItemCount}" Visibility="{Binding DataContext.ShowGroupCountVisibility, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                                                        </StackPanel>
                                                    </Canvas>
                                                </Expander.Header>
                                                <ItemsPresenter/>
                                            </Expander>
                                        </Border>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </DataGrid.GroupStyle>-->

For those interested, we solved resolved this issue by defining separate focus scopes within our application. By default the entire Window is the focus scope. Creating separate ones and making sure we set the focus scopeto something other than the grid before the window deactivates and activates again ensures it goes and comes back snappily. We're assuming there is some undocumented functionality in WPF that produces this phenomena.

WPF Fullscreen Application - Multiple Monitors

7 votes

I've got multiple monitors that are being used with my WPF application. The application runs full screen, and I want to be able to switch which monitor it's on when the user presses a button.

case Key.M:
                    var allScreens = System.Windows.Forms.Screen.AllScreens.ToList();
                    if (this.CurrentScreen < allScreens.Count - 1)
                    {
                        this.CurrentScreen++;
                    }
                    else { this.CurrentScreen = 0; }

                    this.WindowStartupLocation = System.Windows.WindowStartupLocation.Manual;
                    this.Top = allScreens[this.CurrentScreen].Bounds.Top;
                    this.Left = allScreens[this.CurrentScreen].Bounds.Left;
                    break;

I'm trying to do this like so, but this.Left always has the value of (-7). I'm assuming it's not letting my set it because I'm full screen, but I'm not 100% sure. How can I get it to switch to the other monitor in fullscreen?

As a hack, you can change the window state, send it to the other monitor and change the window state back to maximized:

this.WindowState = System.Windows.WindowState.Normal;
this.Left = screen.WorkingArea.Left;
this.Top = screen.WorkingArea.Top;
this.WindowState = System.Windows.WindowState.Maximized;

It works without any undesired effect. Just tested this.

Is there attached property in C# itself?

7 votes

In C# itself, is there something like "attached property" used in WPF?

The short answer is no. The slightly longer answer is that this is a bit of an unfortunate story. We designed "extension properties" for C# 4 and got as far as implementing (but not testing) them when we realized, oh, wait, the thing we designed is not really compatible with WPF-style properties. Rather than redesign and reimplement the feature we ended up cutting it.

The even longer version is here:

http://blogs.msdn.com/b/ericlippert/archive/2009/10/05/why-no-extension-properties.aspx

Updating dependent properties using MVVM

6 votes

Some properties on my viewmodel:

public ObservableCollection<Task> Tasks { get; set; }

public int Count
{
    get { return Tasks.Count; }
}

public int Completed
{
    get { return Tasks.Count(t => t.IsComplete); }
}

What's the best way to update these properties when Tasks changes?

My current method:

public TaskViewModel()
{
    Tasks = new ObservableCollection<Task>(repository.LoadTasks());
    Tasks.CollectionChanged += (s, e) => 
        {
            OnPropertyChanged("Count");
            OnPropertyChanged("Completed");
        };
}

Is there a more elegant way to do this?

With respect to Count, you don't have to do this at all. Simply bind to Tasks.Count and your bindings will get notified of the change by the ObservableCollection.

Completed is a different story, because this is outside of ObservableCollection. Still, from the level of the abstraction/interface, you really want Completed to be a property of that Tasks collection.

For this, I think a better approach would be to create "sub" view-model for your Tasks property:

public class TasksViewModel : ObservableCollection<Task>
{
    public int Completed
    {
        get { return this.Count(t => t.IsComplete); }
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
        if(e.PropertyName == "Count") NotifyCompletedChanged();
    }

    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);
        NotifyCompletedChanged();
    }

    void NotifyCompletedChanged()
    {
        OnPropertyChanged(_completedChangedArgs);
    }
    readonly PropertyChangedEventArgs _completedChangedArgs = new PropertyChangedEventArgs("Completed");
}

This gives you all of the benefits of the ObservableCollection, and effectively makes the Completed property part of it. We still haven't captured only the cases where the number of completed items truly changes, but we have reduced the number of redundant notifications somewhat.

Now the viewmodel just has the property:

public TasksViewModel Tasks { get; set; }

…and you can bind to Tasks, Tasks.Count, and Tasks.Completed with ease.


As an alternative, if you would rather create these other properties on the "main" view-model, you can take this notion of a subclassed ObservableCollection<T> to create one with some method where you can pass in an Action<string> delegate, which would represent raising a property change notification on the main view-model, and some list of property names. This collection could then effectively raise the property change notifications on the view-model:

public class ObservableCollectionWithSubscribers<T> : ObservableCollection<T>
{
    Action<string> _notificationAction = s => { }; // do nothing, by default
    readonly IList<string> _subscribedProperties = new List<string>();

    public void SubscribeToChanges(Action<string> notificationAction, params string[] properties)
    {
        _notificationAction = notificationAction;

        foreach (var property in properties)
            _subscribedProperties.Add(property);
    }


    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
        NotifySubscribers();
    }

    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);
        NotifySubscribers();
    }

    void NotifySubscribers()
    {
        foreach (var property in _subscribedProperties)
            _notificationAction(property);
    }
}

You could even leave the property type as ObservableCollection<Task>.

public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        var tasks = new ObservableCollectionWithSubscribers<Task>();
        tasks.SubscribeToChanges(Notify, "Completed");
        Tasks = tasks;
    }

    public ObservableCollection<Task> Tasks { get; private set; }

    public int Completed
    {
        get { return Tasks.Count(t => t.IsComplete); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    void Notify(string property)
    {
        var handler = PropertyChanged;
        if(handler != null) handler(this, new PropertyChangedEventArgs(property));
    }
}

Negative aspects of dark color scheme (dark background with light text)

6 votes

I am using Light on dark color scheme in my WPF application.
My question is what are the negative aspects of this color scheme and why this color scheme is not widely used?

Please follow this thread at here

Darker color scheme are often used effectively in software that focuses heavily on visual content. For example Adobe Lightroom, Adobe After Effects, Microsoft Expression Blend, and Kaxaml are are interfaces that have a dark color theme. This allows the interface to fade into the background and let the content come alive

Why is it not widely used? I guess it depends on your crowd. For these application it tends to work out great but many people don't like dark interfaces. At my company I created an interface for a very complicated piece of software using a dark UI. It helped to simplify everything and bring attention to the necessary elements at specific times. The problem was, a lot of people complained. "It's too dark," "it doesn't look good on my laptop when I am using in the bright sun" etc. Some people just hate dark interfaces.

Bottom line: if your applciation is very content driven, esp with visual content, consider a dark interface but be prepared for some opposition.

Alternative solution: provide 2 themes, one light and one dark. This is done in Microsoft's Expression Blend (although the light theme looks quite awful, IMO)

Also, it is important to note it is often a bit more difficult to get a dark interface that works well. A little more care must be given to legibility of text on the dark background (i.e. making it bright enough to be legible but not so bright that it is distracting to read.

Hooking into Windows message loop in WPF window adds white border on the inside

6 votes

I am trying to create a WPF window with WindowStyle="None" (for custom buttons and no title) that cannot be resized. Setting ResizeMode to NoResize removes the aero border, which I want to keep.

I could set the min/max size properties and be done with it, except that:

  1. The resize cursors are still visible, and
  2. The window is displayed in response to a user action and fits to its contents. It displays an image, so the size changes.

So, I have a simple scheme that gets me 99% of the way there:

public class BorderedWindowNoResize : Window
{
    [DllImport( "DwmApi.dll" )]
    public static extern int DwmExtendFrameIntoClientArea(
        IntPtr hwnd,
        ref MARGINS pMarInset );

    [DllImport( "user32.dll", CharSet = CharSet.Auto )]
    public static extern IntPtr DefWindowProc(
        IntPtr hWnd,
        int msg,
        IntPtr wParam,
        IntPtr lParam );

    public BorderedWindowNoResize()
    {           
        Loaded += BorderedWindowNoResize_Loaded;
    }

    private void BorderedWindowNoResize_Loaded( object sender, RoutedEventArgs e )
    {           
        IntPtr mainWindowPtr = new WindowInteropHelper( this ).Handle;
        HwndSource mainWindowSrc = HwndSource.FromHwnd( mainWindowPtr );            
        mainWindowSrc.AddHook( WndProc );
    }

    private IntPtr WndProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled )
    {           
        var htLocation = DefWindowProc( hwnd, msg, wParam, lParam ).ToInt32();

        if( msg == (uint)WM.NCHITTEST )
        {
            handled = true;
            switch( htLocation )
            {
                case (int)HitTestResult.HTBOTTOM:
                case (int)HitTestResult.HTBOTTOMLEFT:
                case (int)HitTestResult.HTBOTTOMRIGHT:
                case (int)HitTestResult.HTLEFT:
                case (int)HitTestResult.HTRIGHT:
                case (int)HitTestResult.HTTOP:
                case (int)HitTestResult.HTTOPLEFT:
                case (int)HitTestResult.HTTOPRIGHT:
                    htLocation = (int)HitTestResult.HTBORDER;
                    break;
            }               
        }

        return new IntPtr( htLocation );
    }
}

Basically;

  1. Override the window procedure.
  2. Call the default window procedure.
  3. If the message it is WM_NCHITTEST, check for the border results.
  4. If it is a border, return the regular HTBORDER.

This works as far as allowing me to keep the aero window border and hiding the resize cursor(s), but it adds a ~5 pixel white border to the inside of my window.

In fact, even if I return the default windows procedure result at the top of WndPrc and do nothing else the border is still there. I need a different background color on my window, so this won't work for me.

Any ideas? Thanks in advance as always.

When you add your hook, you should only handle the messages you need to, and ignore the others. I believe you are handling certain messages twice, since you call DefWindowProc, but never set the handled parameter to true.

So in your case, you'd use:

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {

    if (msg == (uint)WM.NCHITTEST) {
        handled = true;
        var htLocation = DefWindowProc(hwnd, msg, wParam, lParam).ToInt32();
        switch (htLocation) {
            case (int)HitTestResult.HTBOTTOM:
            case (int)HitTestResult.HTBOTTOMLEFT:
            case (int)HitTestResult.HTBOTTOMRIGHT:
            case (int)HitTestResult.HTLEFT:
            case (int)HitTestResult.HTRIGHT:
            case (int)HitTestResult.HTTOP:
            case (int)HitTestResult.HTTOPLEFT:
            case (int)HitTestResult.HTTOPRIGHT:
                htLocation = (int)HitTestResult.HTBORDER;
                break;
        }
        return new IntPtr(htLocation);
    }

    return IntPtr.Zero;
}

Also, I'd probably add the hook in an OnSourceInitialized override, like so:

protected override void OnSourceInitialized(EventArgs e) {
    base.OnSourceInitialized(e);

    IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
    HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
    mainWindowSrc.AddHook(WndProc);
}