Best wpf questions in March 2011

Is there any reason to make POCOs into Model objects?

12 votes

If I am generating POCO objects from EntityFramework, and using these to go to/from the WCF server, is there any reason to create client-side Models for the Views & ViewModels to use instead of just using the POCOs directly?

Almost all the MVVM examples I have looked at bind straight to the object returned from the WCF service. Is this good practice? Are there arguments that can be made for actually mapping the POCO to a Model and having the Views/ViewModels working with the Model object instead of the POCO?

The main reason I could think of is validation, however since the EF POCOs are partial classes, they can be expanded on to include validation.

EDIT

Most answers so far have brought up INotifyPropertyChanged as the main reason to build a separate Model. Does your answer change if you are using Self-Tracking entities instead of POCOs which already includes INotifyPropertyChanged? STEs are also partial classes which can be expanded upon to include validation.

Validation is the main reason not to bind directly to a POCO. In addition, if the POCO doesn't already implement INotifyPropertyChanged and other required interfaces, the experience working with the object on the WPF side may be less desirable, and implementing a ViewModel to wrap this makes sense.

Providing a ViewModel to wrap your POCO allows you to encapsulate the logic into ICommand implementations as well as implement required interfaces cleanly.

How do I make a WPF window movable by dragging the extended glass frame?

11 votes

In applications like Windows Explorer and Internet Explorer, one can grab the extended glass areas beneath the title bar and drag windows around.

For WinForms applications, one would simply override the WndProc() handler in their form, process the WM_NCHITTEST window message and trick the system into thinking a click on the glass area was really a click on the title bar by returning HTCAPTION. I've done that in my own WinForms apps to delightful effect.

In WPF, I can also implement a similar WndProc() method and hook it to my WPF window's handle while extending the glass into the client area, like this:

// For use with Aero Glass extensions
private IntPtr hwnd;
private HwndSource hsource;

private void Window_SourceInitialized(object sender, EventArgs e)
{
    try
    {
        if ((hwnd = new WindowInteropHelper(this).Handle) == IntPtr.Zero)
        {
            throw new InvalidOperationException("Could not get window handle for the main window.");
        }

        hsource = HwndSource.FromHwnd(hwnd);
        hsource.AddHook(WndProc);

        AdjustGlassFrame();
    }
    catch (InvalidOperationException)
    {
        FallbackPaint();
    }
}

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case DwmApiInterop.WM_NCHITTEST:
            handled = true;
            return new IntPtr(DwmApiInterop.HTCAPTION);

        default:
            return IntPtr.Zero;
    }
}

The problem is that, since I'm blindly setting handled = true and returning HTCAPTION, clicking anywhere but the window icon or the control buttons causes the window to be dragged. That is, everything highlighted in red below causes dragging. This even includes the resize handles at the sides of the window (the non-client area). My WPF controls, namely the text boxes and the tab control, also stop receiving clicks as a result:

What I want is for only

  1. the title bar, and
  2. the regions of the client area...
  3. ... that aren't occupied by my controls

to be draggable. That is, I only want these red regions to be draggable (client area + title bar):

How do I modify my WndProc() method and the rest of my window's XAML/code-behind, to determine which areas should return HTCAPTION and which shouldn't? I'm thinking something along the lines of using Points to check the location of the click against the locations of my controls, but I'm not sure how to go about it in WPF land.

EDIT [4/24]: one simple way about it is to have an invisible control, or even the window itself, respond to MouseLeftButtonDown by invoking DragMove() on the window (see Ross's answer). The problem is that for some reason DragMove() doesn't work if the window is maximized, so it doesn't play nice with Windows 7 Aero Snap. Since I'm going for Windows 7 integration, it's not an acceptable solution in my case.

Here's something you could try:

Get the location of the mouse click (from the wParam, lParam maybe?), and use it to create a Point (possibly with some kind of coordinate transformation?).

Then call the static method VisualTreeHelper.HitTest(Visual,Point) passing it this and the Point that you just made. The return value will indicate the control with the highest Z-Order. If that's your window, then do your HTCAPTION voodoo. If it's some other control, then...don't.

Good luck!

Is there a way to view how screens look in another language during design time using Expression Blend?

10 votes

I have a WPF application for which I have extracted all the strings in the WPF controls into an .resx file. I also have .resx files for each language supported (.es.resx, .ja.resx, etc.).

Is there an easy/straightforward way to view what the output will look like in the other languages during design time. It would save quite a bit of time to not have to run the application, see which controls needed adjustment, make notes, stop the application, make the changes and repeat.

I would much rather make the changes directly during design time using Blend.

Any ideas?

I went with what was suggested in one of the comments by RQDQ.

  1. Rename foo.resx to foo.copy.resx (using foo as a generic name placeholder)
  2. Make a copy of the language you wish to test and rename it to foo.resx : so, for example, copy foo.fr.resx to foo.fr.copy.resx and then rename to foo.resx to test out how the French translation will look during design time.
  3. Clean and Rebuild the entire Expression Blend solution.
  4. The strings that have been localized will now appear in the controls in the language you are testing; in this example French.

You will now be able to make changes during design time using the translated strings. Just make sure you reverse the process when you finish making your changes or your default language will end up being the language you are testing and making adjustments with.

I am not crazy about this solution but it works. I also hesitate to mark this as the answer as I answered my own question, but I believe it is the correct answer. Any suggestions on this?

{x:Null} vs. Transparent?

9 votes

What's the difference between the following two?

Background="{x:Null}"

and

Background="Transparent"

Transparent will create a brush that is initialized to a transparent color, null will set the property to null, this means that the destination property has not an brush attached. In WPF it's often important to set a brush to an element. If you for example want to track mouse downs in an element, you must set a background. If you don't want to set a solid color (make it opaque), you can use a transparent brush. This can be done with the string value "Transparent".
The difference lies in the manner, how the property will be set. If you assign null for a brush-property, the property will be set really to null. If you set the string "Transparent", the default value-converter that converts string to brushes converts this to a SolidColorBrush with the Color SystemColors.Transparent.

Short version: {x:Null} sets the destination property to null. "Transparent" sets the destination property to a transparent brush.

Update: Thanks to Kent Boogart (see the comments), here an important addition: If the color value for the transparency is #00ffffff, the the element is hit-test visible (accepts mouse downs). If it is set to #00000000, it is also transparent for hit tests.

Ways to improve WPF UI rendering speed

9 votes

In case a screen of a WPF application contains lots of primitive controls, its rendering becomes sluggish. What are the recommended ways to improve the responsiveness of a WPF application in such a case, apart from adding fewer controls and using more powerful videocard?

Is there a way to somehow use offscreen buffering or something like that?

Hi. Our team was faced with problems of rendering performance. In our case we have about 400 transport units and we should render chart of every unit with a lot of details (text labels, special marks, different geometries etc.).

In first our implementations we splitted each chart into primitives and composed whole unit's chart via Binding. It was very sad expirience. UI reaction was extremelly slow.

So we decided to create one UI element per each unit, and render chart with DrawingContext. Although this was much better in performance aspect, we spent about one month improving rendering.

Some advices:

  1. Cache everything. Brushes, Colors, Geometries, Formatted Texts, Glyphs. (For example we have two classes: RenderTools and TextCache. Rendering process of each unit addresses to shared instance of both classes. So if two charts have the same text, its prepation is executed just once.)
  2. Freeze Freezable, if you are planning to use it for a long time. Especially geometries. Complex unfreezed geometries execute HitTest extremelly slow.
  3. Choose the fastest ways of rendering of each primitive. For example, there is about 6 ways of text rendering, but the fastest is DrawingContext.DrawGlyphs.
  4. Use profiler to discover hot spots. For example, in our project we had geometries cache and rendered appropriate of them on demand. It seemed to be, that no improvements are possible. But one day we thought what if we will render geometries one time and cache ready visuals? In our case such approach happened acceptable. Our unit's chart has just several states. When data of chart is changed, we rebuild DrawingVisual for each state and put them into cache.

Of course, this way needs some investments, it's dull and boring work, but result is awesome.

By the way: when we turned on WPF caching option (you could find link in answers), our app hung up.

Firebug like tool for WPF?

8 votes

I'd like to run my program and hover on elements and have a program show me in the visual tree what I am hovering. I tried 'Snoop' but it just shows me a visual tree without the ability of actually hovering on the elements at runtime. Do you guys know of such a tool?

you can do it with Snoop - hover elements at runtime, just click ctrl+shift while hovering with the mouse.

Is there a property grid control in WPF?

8 votes

If not, then why, because Winforms has one?

I did search for a standard one but couldn't find anything.

Also if doesn't exist, what's the best free one?

check out this:

http://wpg.codeplex.com/

WPF/C#: 'Slide to Unlock' feature from iPhone

8 votes

Hi everyone,

I just want to ask for your opinion on how to achieve the 'Slide to Unlock' feature from iPhone using Windows Presentation Foundation.

I already came across to this article: iPhone slide to unlock progress bar (part 1), and wondering if you can give me some other resources for a good head start. Thank you.

I would retemplate a Slider, as this is the closest control, functionality-wise.

You should catch the event of Value_Changed, and if Value == Maximum then the slider is "opened".

Retemplating the control would make it look like your "unlock control" with ease. I'll paste later an example.

-- EDIT -- Have free time at work, so I started it for you. The usage is as follows:

<Grid x:Name="LayoutRoot">
    <Slider Margin="185,193,145,199" Style="{DynamicResource SliderStyle1}"/>
</Grid>

and the ResourceDictionary:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">

    <LinearGradientBrush x:Key="MouseOverBrush" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="#FFF" Offset="0.0"/>
        <GradientStop Color="#AAA" Offset="1.0"/>
    </LinearGradientBrush>


    <LinearGradientBrush x:Key="LightBrush" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="#FFF" Offset="0.0"/>
        <GradientStop Color="#EEE" Offset="1.0"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="#CCC" Offset="0.0"/>
        <GradientStop Color="#444" Offset="1.0"/>
    </LinearGradientBrush>

    <Style x:Key="SimpleScrollRepeatButtonStyle" d:IsControlPart="True" TargetType="{x:Type RepeatButton}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="Transparent"/>
        <Setter Property="IsTabStop" Value="false"/>
        <Setter Property="Focusable" Value="false"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RepeatButton}">
                    <Grid>
                        <Rectangle Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>


    <Style x:Key="ThumbStyle1" d:IsControlPart="True" TargetType="{x:Type Thumb}">
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Thumb}">
                    <Grid Width="54">
                        <Ellipse x:Name="Ellipse" />
                        <Border CornerRadius="10" >
                            <Border.Background>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFFBFBFB" Offset="0.075"/>
                                    <GradientStop Color="Gainsboro" Offset="0.491"/>
                                    <GradientStop Color="#FFCECECE" Offset="0.509"/>
                                    <GradientStop Color="#FFA6A6A6" Offset="0.943"/>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Fill" Value="{StaticResource MouseOverBrush}" TargetName="Ellipse"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Fill" Value="{StaticResource DisabledBackgroundBrush}" TargetName="Ellipse"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="SliderStyle1" TargetType="{x:Type Slider}">
        <Setter Property="Background" Value="{StaticResource LightBrush}"/>
        <Setter Property="BorderBrush" Value="{StaticResource NormalBorderBrush}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Slider}">
                    <Border CornerRadius="14" Padding="4">
                        <Border.Background>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#FF252525" Offset="0"/>
                                <GradientStop Color="#FF5C5C5C" Offset="1"/>
                            </LinearGradientBrush>
                        </Border.Background>
                        <Grid x:Name="GridRoot">
                        <TextBlock Text="Slide to unlock" HorizontalAlignment="Center" VerticalAlignment="Center" />
                        <!-- TickBar shows the ticks for Slider -->

                        <!-- The Track lays out the repeat buttons and thumb -->
                            <Track x:Name="PART_Track" Height="Auto">
                                <Track.Thumb>
                                    <Thumb Style="{StaticResource ThumbStyle1}"/>
                                </Track.Thumb>
                                <Track.IncreaseRepeatButton>
                                    <RepeatButton Style="{StaticResource SimpleScrollRepeatButtonStyle}" Command="Slider.IncreaseLarge" Background="Transparent"/>
                                </Track.IncreaseRepeatButton>
                                <Track.DecreaseRepeatButton>
                                    <RepeatButton Style="{StaticResource SimpleScrollRepeatButtonStyle}" Command="Slider.DecreaseLarge" d:IsHidden="True"/>
                                </Track.DecreaseRepeatButton>
                            </Track>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="TickPlacement" Value="TopLeft"/>
                        <Trigger Property="TickPlacement" Value="BottomRight"/>
                        <Trigger Property="TickPlacement" Value="Both"/>
                        <Trigger Property="IsEnabled" Value="false"/>

                        <!-- Use a rotation to create a Vertical Slider form the default Horizontal -->
                        <Trigger Property="Orientation" Value="Vertical">
                            <Setter Property="LayoutTransform" TargetName="GridRoot">
                                <Setter.Value>
                                    <RotateTransform Angle="-90"/>
                                </Setter.Value>
                            </Setter>
                            <!-- Track rotates itself based on orientation so need to force it back -->
                            <Setter TargetName="PART_Track" Property="Orientation" Value="Horizontal"/>
                        </Trigger>

                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Note that this is a very good start, but it's not everything. I would also define a custom control that derives from slider and that uses this style automatically. Also I would expose a SlideUnlocked event when the user slides all the way right. To finish it all i would also add an animation that moves the Thumb back left in case the user has dragged it right, but not all the way (to imitate iPhone's UX exactly.)

Good luck, and ask away if you don't know how to implement any of the stages i suggested.

Always use MVVM in a WPF app, or are alternative patterns still practical/useful?

7 votes

On page 374 in the book Microsoft .NET Architecting Applications for the Enterprise, there is a chart regarding the evolution of patterns for the presentation layer and their impact on platforms (figure 7-14).

In addition to showing the evolution from the original MVC pattern, to the more modern variants, that chart also shows that the following modern patterns can be applied across the following technologies:

  1. Model2 (MVC)
    • Web Only
  2. Passive View (MVP)
    • Web
    • WinForms
    • WPF
  3. Supervising Controller (MVP)
    • Web
    • WinForms
    • WPF
  4. MVVM (Presentation Model)
    • WPF Only

Note: One other recent pattern of interest lately not in that chart is Presenter First (MVP) which was envisioned to be more accommodating of TDD.


From what I understand if one develops with WPF, then the MVVM pattern is the de facto choice (kind of like Model2 is for web development). That said, it appears nothing prevents one from using Passive View, Supervising Controller, or Presenter First in a WPF app. Such an approach would result in an application that doesn't really care if the front end is WPF, WinForms, or the Web. It appears that these MVP variants allow more flexibility.

However, does aiming for UI-platform-agnostic flexibility (that might not be needed) come at the cost of making WPF development much more difficult and lose out on a portion of the features/power that WPF offers? So much so that the costs outweigh the benefits?

In other words, is MVVM so great that one shouldn't consider other alternatives in WPF applications?

@RS Conley's answer is giving a very broad into to the subject, and I agree with most. The only thing I think differently is in the bottom line.

MVVM is THE architecture for 95% of applications in WPF.

Choosing any other architectures is means settling for something that is less than the best you can get. In RS Conley's situation Passive View might be the best way to go, but that is far from being the normal case.

As a way to understand how MVVM is better, let's see what's he's losing when he's going the PassiveView approach.

Maintainability

In Passive View, the ViewModel knows about the IView, which means that SRP (Single Responsibility Principle) is not kept. The Controller in PassiveView interacts directly with both the Model and the View, and therefor is doing two completely different things!.

Under MVVM, the ViewModel, which is the heart of the application only have one concern, which is to contain the state&logic of the application. The Maintainability of such code is really superior to PassiveView, MVP or MVC treamendously.

It is true that PassiveView is better when it comes to Automated Tests Coverege, but IMHO, good maintainability of code is far more important. Testability helps u make sure you don't break your code, while Maintainability helps you not to build problematic code to begin with.

When it comes to Maintainability, MVVM and PresentationModel are an evolotion of previous UI architectures, and that's because the SRP principle is kept very strictly. Write enough code in MVVM and you'll see what I mean.

Blendability

Another Feature where MVVM is really strong is Blendability. Since all of the application state is preserved within the ViewModel, it is easy to fake data for design time, which allows enourmous boost to productivity. This is impossible to create in PassiveView, MVP or MVC, because in all of those architectures the controller has to actively put data inside the view. In MVVM the data just "jumps" to the View and therefore can be mocked.

Testability

This is indeed a place where PassiveView is superior to MVVM. If 100% Unit Tests coverage of UI is crucial to you then it's a big deal. In most situations however, the coverage that MVVM allows you is more than enough, and you'll usually add another tier of testing using regular UI Testing (which you'd end up doing in PassiveView also btw).

I think that Testability is the less important of the three features. Sorted by importance, it's Maintainability, Blendablilty, and Testability.

Where is MVVM not the right choice?

I've participated in ~ 15 WPF & Silverlight Projects the last year, all of which MVVM fit perfectly. I think that in places where the presentation logic is extremely big, such as in games, MVVM might not be the right choice. Other than games, I can't really think of an application category that wouldn't go best with MVVM, other than special situations like RS Conley mentioned.

How to remove the System Menu in WPF?

7 votes

According to this MSDN page, if I were using Window, then I could disable the control box in the top left hand corner by setting it to false. Like this: this.ControlBox = false;

The ControlBox has Maximize, Minimize, Restore and Close options
I want to disable this box in my own application

But since I'm using RibbonWindow instead of Window, how would I disable the control box in this situation?

This question is very related, but I'm looking to disable the SystemMenu all the time, not just to prevent the Alt+Space. This is because (I think) the action listener for the SystemMenu in the top left hand corner blocks a clickable UI element in my XAML.

I should note that this is not a problem with Windows Server 2003, but when the application is opened in Windows 7, the SystemMenu/ControlBox interferes with the UI element in the top left corner.

Additionally, I've found that interfering with the system menu usually results in the buttons in the top right hand corner of the application being deactivated, but I don't want that to happen.

Thanks for the link Eammonn. I Think what that person was trying to do is disable the [X] button in the top right hand corner, not the menu in the top left hand corner, but I could be wrong. The reason I don't think it'll work is that they're using <Window x:Class= and I'm using <dc:RibbonWindow x:Class=. Does this make a difference?

The WPF window is not a System.Windows.Forms object, so this.ControlBox = false would not work anyway. There is no explicit property to do this in wpf but see this article

http://winsharp93.wordpress.com/2009/07/21/wpf-hide-the-window-buttons-minimize-restore-and-close-and-the-icon-of-a-window/

It describes how you can remove the WPF version of the control box.

WPF controls are derived from System.Windows.Controls and not System.Windows.Forms

Page vs Window in WPF?

7 votes

What is different between page and window in WPF when you are adding a new file in the solution explorer?

Pages are intended for use in Navigation applications (usually with Back and Forward buttons, e.g. Internet Explorer). Pages must be hosted in a NavigationWindow or a Frame

Windows are just normal WPF application Windows, but can host Pages via a Frame container

What is the easy way to set spacing between items in StackPanel?

7 votes

Is there an easy way to set default space between items inside StackPanel so I'll don't have to set Margin property on each item?

if all the controls are the same then do as IanR suggested and implement a Style that catches that control. if it's not then you can't create a default style to a base class because it just won't work.

the best way for situations like these is to use a very neat trick - attached properties (aka Behaviors in WPF4)

you can create a class that has an attached property, like so:

public class MarginSetter
{
    public static Thickness GetMargin(DependencyObject obj)
    {
        return (Thickness)obj.GetValue(MarginProperty);
    }

    public static void SetMargin(DependencyObject obj, Thickness value)
    {
        obj.SetValue(MarginProperty, value);
    }

    // Using a DependencyProperty as the backing store for Margin.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MarginProperty =
        DependencyProperty.RegisterAttached("Margin", typeof(Thickness), typeof(MarginSetter), new UIPropertyMetadata(new Thickness(), CreateThicknesForChildren));

    public static void CreateThicknesForChildren(object sender, DependencyPropertyChangedEventArgs e)
    {
        var panel = sender as Panel;

        if (panel == null) return;

        foreach (var child in panel.Children)
        {
            var fe = child as FrameworkElement;

            if (fe == null) continue;

            fe.Margin = MarginSetter.GetMargin(panel);
        }
    }


}

now, to use it, all you need to do is to attach this attached property to any panel you want, like so:

<StackPanel local:MarginSetter.Margin="10">
    <Button Content="hello " />
    <Button Content="hello " />
    <Button Content="hello " />
    <Button Content="hello " />
</StackPanel>

Completely reusable of course.

Custom ObservableCollection<T> or BindingList<T> with support for periodic notifications

7 votes

Summary

I have a large an rapidly changing dataset which I wish to bind to a UI (Datagrid with grouping). The changes are on two levels;

  • Items are frequently added or removed from the collection (500 a second each way)
  • Each item has a 4 properties which will change up to 5 times in its lifetime

The characteristics of the data are as follows;

  • There are ~5000 items in the collection
  • An item may, within a second, be added then have 5 property changes and then be removed.
  • An item may also remain in some interim state for a while and should be displayed to the user.

The key requirement which I'm having problems with;

  • The user should be able to sort the dataset by any property on the object

What I would like to do;

  • Update the UI only every N seconds
  • Raise only the relevant NotifyPropertyChangedEvents

If item 1 has a property State which moves from A -> B -> C -> D in the interval I need/want only one 'State' change event to be raised, A->D.

I appreciate a user doesn't need to have the UI updated thousands of times a second. if an item is added, has its state changed and is removed all within the window of N seconds between UI updates it should never hit the DataGrid.

DataGrid

The DataGrid is the component which I am using to display the data. I am currently using the XCeed DataGrid as it provides dynamic grouping trivially. I am not emotionally invested in it, the stock DataGrid would be fine if I could provide some dynamic grouping options (Which includes the properties which change frequently).

The bottleneck in my system is currently in the time taken to re-sort when an item's properties change

This takes 98% of CPU in the YourKit Profiler.

A different way to phrase the question

Given two BindingList / ObservableCollection instances which were initially identical but the first list has since had a series of additional updates (which you can listen for), generate the minimal set of changes to turn one list into the other.

External Reading

What I need is an equivalent of this ArrayMonitor by George Tryfonas but generalized to support adding and removing of items (they will never be moved).

NB I would really appreciate someone editing the title of the question if they can think of a better summary.

EDIT - My Solution

The XCeed grid binds the cells directly to the items in the grid whereas the sorting & grouping functionality is driven by the ListChangedEvents raised on the BindingList. This is slightly counter intuitive and ruled out the MontioredBindingList below as the rows would update before the groups.

Instead I wrap the items themselves, catching the Property changed events and storing them in a HashSet as Daniel suggested. This works well for me, I periodically iterate over the items and ask them to notify of any changes.

MonitoredBindingList.cs

Here is my attempt at a binding list which can be polled for update notifications. There are likely some bugs with it as it was not useful to me in the end.

It creates a queue of Add/Remove events and keeps track of changes via a list. The ChangeList has the same order as the underlying list so that after we've notified of the add/remove operations you can raise the changes against the right index.

/// <summary>
///  A binding list which allows change events to be polled rather than pushed.
/// </summary>
[Serializable]

public class MonitoredBindingList<T> : BindingList<T>
{
    private readonly object publishingLock = new object();

    private readonly Queue<ListChangedEventArgs> addRemoveQueue;
    private readonly LinkedList<HashSet<PropertyDescriptor>> changeList;
    private readonly Dictionary<int, LinkedListNode<HashSet<PropertyDescriptor>>> changeListDict;

    public MonitoredBindingList()
    {
        this.addRemoveQueue = new Queue<ListChangedEventArgs>();
        this.changeList = new LinkedList<HashSet<PropertyDescriptor>>();
        this.changeListDict = new Dictionary<int, LinkedListNode<HashSet<PropertyDescriptor>>>();
    }

    protected override void OnListChanged(ListChangedEventArgs e)
    {
        lock (publishingLock)
        {
            switch (e.ListChangedType)
            {
                case ListChangedType.ItemAdded:
                    if (e.NewIndex != Count - 1)
                        throw new ApplicationException("Items may only be added to the end of the list");

                    // Queue this event for notification
                    addRemoveQueue.Enqueue(e);

                    // Add an empty change node for the new entry
                    changeListDict[e.NewIndex] = changeList.AddLast(new HashSet<PropertyDescriptor>());
                    break;

                case ListChangedType.ItemDeleted:
                    addRemoveQueue.Enqueue(e);

                    // Remove all changes for this item
                    changeList.Remove(changeListDict[e.NewIndex]);
                    for (int i = e.NewIndex; i < Count; i++)
                    {
                        changeListDict[i] = changeListDict[i + 1];
                    }

                    if (Count > 0)
                        changeListDict.Remove(Count);
                    break;

                case ListChangedType.ItemChanged:
                    changeListDict[e.NewIndex].Value.Add(e.PropertyDescriptor);
                    break;
                default:
                    base.OnListChanged(e);
                    break;
            }
        }
    }

    public void PublishChanges()
    {
        lock (publishingLock)
            Publish();
    }

    internal void Publish()
    {
        while(addRemoveQueue.Count != 0)
        {
            base.OnListChanged(addRemoveQueue.Dequeue());
        }

        // The order of the entries in the changeList matches that of the items in 'this'
        int i = 0;
        foreach (var changesForItem in changeList)
        {
            foreach (var pd in changesForItem)
            {
                var lc = new ListChangedEventArgs(ListChangedType.ItemChanged, i, pd);
                base.OnListChanged(lc);
            }
            i++;
        }
    }
}

We are talking about two things here:

  1. The changes to the collection. This raises the event INotifyCollectionChanged.CollectionChanged
  2. The changes to the properties of the items. This raises the event INotifyPropertyChanged.PropertyChanged

The interface INotifyCollectionChanged needs to be implemented by your custom collection. The interface INotifyPropertyChanged needs to be implemented by your items. Furthermore, the PropertyChanged event only tells you which property was changed on an item but not what was the previous value.
This means, your items need to have a implementation that goes something like this:

  • Have a timer that runs every N seconds
  • Create a HashSet<string> that contains the names of all properties that have been changed. Because it is a set, each property can only be contained one or zero times.
  • When a property is changed, add its name to the hash set if it is not already in it.
  • When the timer elapses, raise the PropertyChanged event for all properties in the hash set and clear it afterwards.

Your collection would have a similar implementation. It is however a little bit harder, because you need to account for items that have been added and deleted between to timer events. This means, when an item is added, you would add it to a hash set "addedItems". If an item is removed, you add it to a "removedItems" hash set, if it is not already in "addedItems". If it is already in "addedItems", remove it from there. I think you get the picture.

To adhere to the principle of separation of concerns and single responsibility, it would be even better to have your items implement INotifyPropertyChanged in the default way and create a wrapper that does the consolidation of the events. That has the advantage that your items are not cluttered with code that doesn't belong there and this wrapper can be made generic and used for every class that implements INotifyPropertyChanged.
The same goes for the collection: You can create a generic wrapper for all collections that implement INotifyCollectionChanged and let the wrapper do the consolidation of the events.

Images are not sharp in selected TabItem

7 votes

I have a TabControl. The header of each TabItem contains a StackPanel with an icon and a Label.

<TabControl>
  <TabItem>
    <TabItem.Header>
      <StackPanel Orientation="Horizontal">
        <Image Source="/LoginPanel;component/Icons/icoLogin.ico"</Image>
        <Label VerticalContentAlignment="Center">Login</Label>
        </StackPanel>
      </TabItem.Header>
    </TabItem.Header>
    <!--some further code here-->
  <TabItem>
  <!--some further code here-->
<TabControl>

Each icon in each non-selected TabItem is displayed as expected. The Icon in the currently selected TabItem is somewhat cloudy. If I switch to another Tab, the de-selected Tab-Icon becomes clear; the new selected Icon becomes cloudy.

I already tried the following to solve this:

SnapsToDevicePixels="True"

but nothing happens

or

Width="32" Height="32"

or

Stretch="None"

to prevent scaling. All of this without any effect. Can some please give me a hint? Thanks in advance

This works for me:

<Image Source="/LoginPanel;component/Icons/icoLogin.ico"
       RenderOptions.BitmapScalingMode="NearestNeighbor"</Image>

Should I compress in-memory C# objects for better performance?

7 votes

I have an application (C#, WPF) that displays many financial charts with live data streaming from server. The data that is collected in-memory may grow to be a bit large, and I don't want to keep any data on disk.

Since the historical data itself doesn't change, but only added to, will it make sense to keep that data (which is stored in a collection object) in some compressed format?

Is it possible and can anyone recommend a good practice for it if so?

UPDATE

Some notes about performance and tradeoff: I am aware that compression will add a delay accessing the data, but, the user only needs fast updates on new data arriving. When accessing the data that was already rendered (for example, to study or re-render it) he doesn't require a quick response.

Compressing and decompressing will make your application slower so for performance (speed) it is not a good option. Compression is only useful when you are worried about available memory. It might be easier to store/swap the data to a temp folder.

The key to performance is measuring. Only take action when you have crunched the numbers.

Cross Platform Alternatives to WPF

6 votes

Hi, all, I'm thinking of porting my application from VB.net to the C# based MONO project, so it can run on both Windows and Mac. However, I am in need of a Mac-friendly alternative to WPF. It has to have very similar functionality. QML (by QT) is not a viable option, as it costs far too much money for us.

Miguel de Icaza recommends MonoMac as a substitute to WPF for Mac as stated here. As he explains there is no plan to implement WPF in mono.

You will have to build two different UI on top of a common engine, but that's generally a good separation of concerns.

There is also a possible common code base for UI with GTK#.