Best wpf questions in July 2011

Weird XAML parsing error when trying to set TextBox.IsReadOnly

13 votes

I've managed to reduce this to a simple test case. An exception is thrown during the parsing of this XAML using XamlReader.Parse():

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DockPanel.Resources>
        <Style TargetType="TextBox">
            <Style.Triggers>
                <Trigger Property="IsReadOnly" Value="True">
                    <Setter Property="Background" Value="#FFEEEEEE" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </DockPanel.Resources>


    <TextBox IsReadOnly="True" />
</DockPanel>

The exception message is:

Cannot set unknown member 'System.Windows.Controls.TextBox.IsReadOnly'. Line number '13' and line position '11'.

If I don't set IsReadOnly on the TextBox, it parses fine. It also parses fine if I remove the style trigger.

Can anyone shed some light on this? I'm rather new to WPF.

UPDATE:
Here's the unit test I'm using to reproduce this (it's failing on my PC):

[TestMethod]
public void TestIsReadOnlyOnTextBox()
{
    // Arrange
    var xaml =
@"<DockPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
    <DockPanel.Resources>
        <Style TargetType=""TextBox"">
            <Style.Triggers>
                <Trigger Property=""IsReadOnly"" Value=""True"">
                    <Setter Property=""Background"" Value=""#FFEEEEEE"" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </DockPanel.Resources>


    <TextBox IsReadOnly=""True"" />
</DockPanel>
";

    // Act
    try {
        var root = XamlReader.Parse(xaml);
    }
    catch (XamlParseException ex) {
        Assert.Fail(ex.Message);
    }

    // If we get here, test passes
}

UPDATE 2:
I was originally referencing just PresentationFramework v4.0.30319. Adding references to PresentationCore, System.Xaml, and WindowsBase has no effect.

.NET version of project is 4 (full, not client profile).

UPDATE 3:
Arg, this works fine in ExpressionBlend 3.0.1927.0 and XamlPadX 4. As reported by AresAvatar, it seems to only fail when parsed with XamlReader.Parse() or XamlReader.Load()!

Short answer, clearly this is a bug. The following can be used as a workaround.

Update, workaround 2

Even just executing the following line before XamlReader.Parse(xaml) fixes the problem, still clueless as to why though..

XamlReader.Parse(@"<TextBox xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
                            xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
                            IsReadOnly=""True""/>");
var root = XamlReader.Parse(xaml);

Workaround 1
Using Boolean in mscorlib instead of True in the Trigger seems to fix the problem for good. The following xaml does not throw an exception in XamlReader.Parse

var xaml =
@"<DockPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
             xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
             xmlns:s=""clr-namespace:System;assembly=mscorlib"" >
    <DockPanel.Resources>
        <s:Boolean x:Key=""BooleanTrue"">True</s:Boolean>
        <Style TargetType=""TextBox"">
            <Style.Triggers>
                <Trigger Property=""IsReadOnly"" Value=""{StaticResource BooleanTrue}"">
                    <Setter Property=""Background"" Value=""#FFEEEEEE"" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </DockPanel.Resources>      
    <TextBox IsReadOnly=""True"" />
</DockPanel>";

Some research details..

I did some testing of this weird problem.

First I included the working DockPanel in Xaml and saved it with

string xaml = XamlWriter.Save(theDockPanel);

just to see if that piece of xaml was working with XamlReader.Parse, and it did.

Then I made small changes to the generated xaml (and reverted once the exception came back) until I got as close as possible to the original. The weird part is that once this xaml has been parsed, the original works as well.

The part that made it working seems to be using <s:Boolean>True</s:Boolean> instead of True.

var modifiedXaml = @"<DockPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
                                xmlns:s=""clr-namespace:System;assembly=mscorlib"" 
                                xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
                <DockPanel.Resources>
                    <s:Boolean x:Key=""BooleanTrue"">True</s:Boolean>
                    <Style TargetType=""TextBox"">
                        <Style.Triggers>
                            <Trigger Property=""IsReadOnly"" Value=""{StaticResource BooleanTrue}"">
                                <Setter Property=""Background"" Value=""#FFEEEEEE"" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </DockPanel.Resources>
                <TextBox IsReadOnly=""True"" />
            </DockPanel>";

var originalXaml = @"<DockPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
                                xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
                <DockPanel.Resources>
                    <Style TargetType=""TextBox"">
                        <Style.Triggers>
                            <Trigger Property=""IsReadOnly"" Value=""True"">
                                <Setter Property=""Background"" Value=""#FFEEEEEE"" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </DockPanel.Resources>
                <TextBox IsReadOnly=""{Binding}""/>
            </DockPanel>";
try
{
    // If this line is executed, no `XamlParseException` is thrown
    var root = XamlReader.Parse(modifiedXaml);
    var root2 = XamlReader.Parse(originalXaml);
}
catch (XamlParseException ex)
{

}

I'll update again if I find something more on this..

Do you need to remove an event handler in the destructor?

12 votes

I use some UserControls which get created and destroyed within my application during runtime (by creating and closing subwindows with these controls inside).
It's a WPF UserControl and inherits from System.Windows.Controls.UserControl. There is no Dispose() method I could override.
PPMM is a Singleton with the same lifetime as my application.
Now in the constructor of my (WPF) UserControl, I add an event handler:

    public MyControl()
    {
        InitializeComponent();

        // hook up to an event
        PPMM.FactorChanged += new ppmmEventHandler(PPMM_FactorChanged);
    }

I got used to removing such event handler in the destructor:

    ~MyControl()
    {
        // hook off of the event
        PPMM.FactorChanged -= new ppmmEventHandler(PPMM_FactorChanged);
    }

Today I stumbled upon this and wondered:

1) Is this neccessary? Or does the GC take care of it?

2) Does this even work? Or would I have to store the newly created ppmmEventHandler?

I'm looking forward to your answers.

Since PPMM is a long-lived object (singleton), then this code doesn't make much sense.

The problem here is that as long as that event handler is referencing the object, it will not be eligible for garbage collection, as least as long as that other object that owns the event is alive.

As such, putting anything in the destructor is pointless, as either:

  1. The event handler has already been removed, thus the object became eligible for garbage collection
  2. The event handler is not removed, the owning object is not eligible for garbage collection, and thus the finalizer will never get called
  3. Both objects are eligible for garbage collection, in which case you should not access that other object at all in the finalizer since you don't know its internal state

In short, don't do this.

Now, a different argument could be said about adding such code to the Dispose method, when you're implementing IDisposable. In that case it fully makes sense since its usercode that is calling Dispose, at a predefined and controlled point.

The finalizer (destructor), however, is only called when the object is eligible for garbage collection and has a finalizer, in which case there is no point.

As for question nbr. 2, which I take as "Can I unsubscribe from events like that", then yes, you can. The only time you need to hold on to the delegate you used to subscribe with is when you're constructing the delegate around an anonymous method or a lambda expression. When you're constructing it around an existing method, it will work.


Edit: WPF. right, didn't see that tag. Sorry, the rest of my answer doesn't make much sense for WPF and since I am no WPF-guru, I can't really say. However, there's a way to fix this. It's entirely legal here on SO to poach the content of another answer if you can improve it. So if anyone knows how to properly do this with a WPF usercontrol, you're free to lift the entire first section of my answer and add the relevant bits of WPF.

Edit: Let me respond to the question in the comment inside here as well.

Since the class in question is a user-control, its lifetime will be tied to a form. When the form is closing, it will dispose of all child controls that it owns, in other words, there is already a Dispose method present here.

The correct way for a user control to handle this, if it manages its own events, is to unhook the event handlers in the Dispose method.

(rest removed)

WPF vs Winforms for .Net Newbies

10 votes

I'm very new to Microsoft .Net framework
My question is which is easier and faster to learn Winforms or WPF?
Thanks.

Easier and faster? Almost certainly winforms.

WPF is tricker to quantify - it is more complex yet more powerful, but to be honest I've lost track of it's future... Is it killed? Who knows... Xaml certainly has life in mobile (WP7), though.

IMO though, your time is better spent learning web UI - some jQuery or HTML5.

Can I store an Object inside a button in C#

9 votes

I am creating Buttons dynamically in my code, is there a way I can store a custom object in my button so I can use it when I press this button ?

Consider using the command pattern and bind a command to the button's Command property and use the CommandParameter Property to store your object.

When the button is clicked the Execute method of your command will be invoked using the CommandParameter (containing your object) as parameter.

It would be good to know the scenario you are working at. Generating XAML by code is a sign you may be on the wrong track as long as you are not building custom controls.

Most things can be accomplished via data bindings and repeater controls such as listboxes, menus, datagrids, etc. Are you familar with the MVVM pattern?

New Windows Application - What language?

7 votes

We are currently in pre phase of developing a desktop application for windows. But when hearing all the latest discussions on Windows 8, Silverlight, WPF, Jupiter I don't know what to believe anymore. Is it wrong starting a new project with WPF now? Should I switch to Silverlight? Or should I wait until more details of Windows 8 comes out?

Interesting topic, main point is if you do it windows based (Winforms or WPF) or web based (ASP.NET MVC or Silverlight).

For a new business application we are just starting now, business wants windows based because they want more features, IT wants web based to have no deployment issues and easier support on a centralized server instead of trying to figure out what the client machines has...

In fact I believe WPF is ready for LOB (even if there are still less third party controls comparing to winforms).

I would not invest on SL because still requires a plugin and with MVC / Ajax and HTML5 you can do the same and more with no plugins required and having same UI running on all browsers and platforms ( I focus very much of have my web app running also in iPad and Android tablets with no changes )...

Main point is the architecture, how you distribute it across servers and tiers so to have well distributed workloads and good reliability... then if you have a UI windows based or web based, as long these UIs consume the same server components exposed as WCF end points for example... is more a "kind" of detail...

New instance on Taskbar middle-click / shift+click

7 votes

In Windows 7, some programs allow the user to launch multiple instances by middle clicking / shift+clicking on the taskbar item (ex: Paint, Notepad, Visual Studio...)

However, none of the applications I create exhibit this behavior. The middle click function will make the button glow, but fail to open another instance unless I pin the program to the taskbar first (which is not required for Paint/Notepad/etc to still be able to open new instances)

What is the secret to allowing a WPF application to properly create new instances like the above listed programs?

I've tried searching, but I have only come up with jumplist/tasks or single-instance program results.

The secret here is called shortcut. To get this behavior, you have to create a shortcut to your program, to the start menu, the desktop or the taskbar. When a shortcut is present, Windows finds it from the running program, even if it has been started by a totally different way (command line, explorer, etc...) and middle clicking or shift-clicking on the open program icon in the taskbar simply executes the shortcut.

This is easily verifiable: remove the shortcut for Notepad from the start menu and you won't be able to middle click it anymore! Change the shortcut by adding an argument to it (a text file path) and the file will be opened in notepad by shift-clicking on the taskbar icon.

C# PropertyPath

6 votes
new PropertyPath("(RotateTransform.Angle)") 

why do we need the brackets around RotateTransform.Angle? And why sometimes we don't need brackets around it?

Cheers

According to this: MSDN Binding.Path the brackets indicate that the path refers to an attached property. Since attached-properties are not declared on the dependency-object itself, the binding might need an indicator to distinct dependency-properties from attached properties.

/edit: sorry, gave you a german link, see my update.

"The operation completed successfully" exception

6 votes

I have a custom method that finds the largest size to use for a given string and font to fill a given box without cutting off the text. To test it, I created a service that cycles through a few different strings and a few different fonts and does batches of them in a Parallel.For loop. When this service is running, all the CPU cores on a system are at %90-%100. After running for 8 or 9 hours, it will start throwing exceptions. It will still work most of the time, but there will be the occasional exception or burst of exceptions.

The innermost exception has the message "The operation completed successfully" and is originating from the WidthIncludingTrailingWhitespace accessor on a FormattedText object. The call stack looks like this:

   at MS.Win32.UnsafeNativeMethods.RegisterClassEx(WNDCLASSEX_D wc_d)
   at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   at System.Windows.Threading.Dispatcher..ctor()
   at System.Windows.Threading.Dispatcher.get_CurrentDispatcher()
   at System.Windows.Media.TextFormatting.TextFormatter.FromCurrentDispatcher(TextFormattingMode textFormattingMode)
   at System.Windows.Media.FormattedText.LineEnumerator..ctor(FormattedText text)
   at System.Windows.Media.FormattedText.DrawAndCalculateMetrics(DrawingContext dc, Point drawingOffset, Boolean getBlackBoxMetrics)
   at System.Windows.Media.FormattedText.get_Metrics()
   at System.Windows.Media.FormattedText.get_WidthIncludingTrailingWhitespace()
   ...My Library Here...

In researching this, I found that undisposed drawing objects (Graphics, Icons, etc) are a common cause for this, but I could not find any Disposable objects in use. The text sizing code uses WPF classes (FontFamily, FormattedText, and Typeface) and none of them implement IDisposable.

I have perfmon monitoring the process and while memory use, handle count, and thread count do vary quite a bit, they never skyrocket out of control. This tells me it probably isn't a handle leak. What else could it be?

UPDATE: I have been running the test for a few days now with one significant change: it is doing a regular for instead of a parallel for. It hasn't crashed yet and perfmon shows horizontal lines with very little variance. Perhaps this is an issue with parallelization and not WPF text rendering?

I replaced the Parallel.For with the task scheduler found here: http://code.msdn.microsoft.com/Samples-for-Parallel-b4b76364/sourcecode?fileId=25353&pathId=939364525

I have not seen the problem since the switch.

This really doesn't answer the question but it does seem to be a working alternative.

WPF Binding within text literal

6 votes

Is there any way to do this in a binding expression:

Text="Hello {Binding CurrentUser}"

ie:

<TextBlock HorizontalAlignment="Right" Foreground="#3163AB" Margin="0,0,0,5" 
    FontWeight="Bold" Text="Hello {Binding CurrentUser}" />

Obviously I could break it out into two separate textblocks, but this would be much nicer.

As of .NET 4, the Text property of a Run can be bound. I use it all the time:

<TextBlock>
    Hello
    <Run Text="{Binding CurrentUser}" />,
    how are you?
</TextBlock>

The StringFormat method is nice, but using a Run with a binding allows the use of Value Converters.

How to pick a background color depending on font color to have proper contrast

6 votes

I don't know much about color composition, so I came up with this algorithm that will pick a background color based on the font color on a trial an errors basis:

public class BackgroundFromForegroundColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is Color))
            return value;
        Color color = (Color)value;
        if (color.R + color.G + color.B > 550)
            return new SolidColorBrush(Colors.Gray);
        else if (color.R + color.G + color.B > 400)
            return new SolidColorBrush(Colors.LightGray);
        else
            return new SolidColorBrush(Colors.White);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I did some googling about this, but I haven't found anything very formal about the different ways a background color can be calculated to get a good contrast effect with the font color.

So my question is: is there a more "formal" approach to pick a good background to get a good contrast? Alternatively, how would you handle picking a background color with the sole intent of having your text as readable as possible whatever its font color?

Quick update

A bit more context: I'm simply trying to show a preview of some text (eg "The quick brown fox jumps over the lazy dog") where the user picks the font color, weight, font, etc. I am however interested to see what can be done, whether it's super simple, or more complex.

Final edit

I decided to go with what H.B. suggested: it seems to work fine with all colors I tried unlike with my previous algorithm were the foreground would not always contrast properly with the background. I would've been curious to see if there is formula that gives you an "optimal" background for a given foreground, but for what I need black/white works just fine. This is my code in its current form:

public class BackgroundFromForegroundColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is Color))
            return value;
        Color color = (Color)value;
        double Y = 0.2126 * color.ScR + 0.7152 * color.ScG + 0.0722 * color.ScB;
        return Y > 0.4 ? Brushes.Black : Brushes.White;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

There are some methods of calculating the brighness of a colour, based on that you could just take a black or white background and you would get a decent readability. See luma for example

Y = 0.2126 R + 0.7152 G + 0.0722 B

I think the threshold would be 0.5 if you use normalized input values (0.0 - 1.0), but it's been a while since i used this...

Edit: Example convert implementation sketch:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    var c = (Color)value;
    var l = 0.2126 * c.ScR + 0.7152 * c.ScG + 0.0722 * c.ScB;
    if (l < 0.5)
    {
        return Brushes.White;
    }
    else
    {
        return Brushes.Black;
    }
}

The threshold may actually be a bit dependent on the display and personal preference, i for one would prefer something lower resulting in a bigger share of black backgrounds.

WPF memory leak

5 votes

I have a simple wpf application. In main window i have stack panel and 2 buttons. First button adds 100 my user controls (without any data bindings, events, bitmaps), and second removes all of them from panel and calls GC.Collect(). And there are some problems: 1. After i clicked "remove" button first time not all my memory releases, and I must click it few times to release more memory. 2. After 5 - 10 min memory releases but few megabytes dont.

for example after my app starts it takes ~22mb when i adding 500 controls - ~60mb after i clicked "remove" button first time - ~55mb (I wait some time, memory not deallocated) i click few times and memory fell down to 25mb, I dont understand this, I am new in WPF, and maybe i miss something I want to release memory immediately.

<Window x:Class="WpfApplication10.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="385" Width="553">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="25" />
        <RowDefinition Height="240*" />
        <RowDefinition Height="25" />
    </Grid.RowDefinitions>
    <Grid 
            Name="border1" 
            Grid.Row="1"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch" >
        <ScrollViewer VerticalAlignment="Stretch"
                      Name="scrollViewer1" 
                      HorizontalAlignment="Stretch">
            <StackPanel 
                Margin="3,3,3,3"
                Background="Transparent"
                VerticalAlignment="Stretch"
                Name="activityStackPanel"
                HorizontalAlignment="Stretch">
            </StackPanel>
        </ScrollViewer>
    </Grid>
    <Button Content="Button" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="12,0,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
    <Button Content="Button" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="141,0,0,0" Name="button2" VerticalAlignment="Top" Width="75" Click="button2_Click" />
    <Label Content="Label" Grid.RowSpan="2" Height="28" HorizontalAlignment="Left" Margin="34,0,0,0" Name="label1" VerticalAlignment="Top" />
</Grid>

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

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            int N = 100;
            //var r = new ActivityStatisticItem("111", "222", DateTime.Now, "333", 1);
            for (int i = 0; i < N; i++)
            {
                activityStackPanel.Children.Add(new UserControl1());
            }

            label1.Content = activityStackPanel.Children.Count;
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
           activityStackPanel.Children.Clear();

            label1.Content = activityStackPanel.Children.Count;

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
        }
    }
}
<UserControl x:Class="WpfApplication10.UserControl1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         Background="Transparent"
         Margin="0,2,0,2"
         MinHeight="80"
         MinWidth="130"
         MaxHeight="80">
<Grid Width="441">
    <Grid.RowDefinitions>
        <RowDefinition Height="40" Name="rowTop" />
        <RowDefinition Height="40" Name="rowBottom"/>
    </Grid.RowDefinitions>
    <Border BorderBrush="Gray" 
            BorderThickness="1" 
            HorizontalAlignment="Stretch" 
            Background="LightGreen"
            Name="contactPanel" 
            CornerRadius="3,3,3,3"
            VerticalAlignment="Stretch" Panel.ZIndex="1" >
        <Grid
            VerticalAlignment="Stretch" 
            Name="grid1" 
            Margin="3,0,3,0"
            HorizontalAlignment="Stretch">

            <Label Content="Contact" Height="15" HorizontalAlignment="Left" Margin="15,3,0,0" Name="headerLabel" Padding="0" VerticalAlignment="Top" FontSize="10" FontWeight="DemiBold"/>
            <Label Content="00/00/0000 00:00:00" Height="15" HorizontalAlignment="Left" Margin="13,18,0,0" Name="timeLabel" Padding="0" VerticalAlignment="Top" FontSize="10" Width="100" FontWeight="DemiBold" />
            <Label Content="00:00:00" Height="15" HorizontalAlignment="Right" Margin="0,18,0,0" Name="durationLabel" Padding="0" VerticalAlignment="Top" FontSize="10" Width="38" FontWeight="DemiBold"/>

            <!--<Image Height="12" HorizontalAlignment="Left" Margin="0,3,0,0" Name="directionPictureBox" Stretch="Fill" VerticalAlignment="Top" Width="12"  />
            <Image Height="12" HorizontalAlignment="Right" Margin="0,20,41,0" Name="timerImage" Stretch="Fill" VerticalAlignment="Top" Width="12"          />
            <Image Height="12" HorizontalAlignment="Left" Margin="0,20,0,0" Name="dateTimeImage" Stretch="Fill" VerticalAlignment="Top" Width="12"       />-->


        </Grid>
    </Border>
    <Border BorderBrush="Gray" 
            BorderThickness="1,0,1,1" 
            Grid.Row="1" 
            Background="White"
            HorizontalAlignment="Stretch" 
            Margin="10,0,10,0" 
            Name="detailsPanel" 
            CornerRadius="0,0,3,3"
            VerticalAlignment="Stretch">
        <Grid HorizontalAlignment="Stretch" 
              Name="grid2" 
              Margin="3,0,3,0"
              VerticalAlignment="Stretch">
            <Label Content="Label" Height="15" HorizontalAlignment="Stretch" FontSize="9" Padding="0" Margin="0,3,0,0" Name="numberRadLabel" VerticalAlignment="Top" />
            <Label Content="Label" Height="15" HorizontalAlignment="Stretch" FontSize="9"  Padding="0" Margin="0,18,0,0" Name="queueRadLabel" VerticalAlignment="Top" />

        </Grid>
    </Border>
</Grid>

In user control i have only

         public UserControl1()
         {
            InitializeComponent();
         }

I want to release memory immediately.

Don't. Trust GC.

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Don't. Trust GC.

After 5 - 10 min memory releases

Didn't I say trust GC?


  • Garbage collection model will make sure the unwanted managed memory in your system is released (which includes almost all of your controls memory). It uses an algorithm for optimising which includes generations, free memory available, possibly CPU available... so GC.Collect() will interfere with it.

  • GC.Collect() is asynchronous so no immediate effect.

  • The only resource you need to be careful is the unmanaged resource which usually is handled by Dispose Pattern. Otherwise don't mess with GC, it does its job very well.

WPF MVVM implementing the viewmodel to reflect the base model?

5 votes

Something that has been confusing me for a while now with WPF MVVM is for example, when I have a base model containing nothing but a few properties and some validation code and I then build a view model around this base model, how should the view model be structured.

For Example:

Base Model ->

Imports ModellingHelper
Imports FTNHelper
Imports System.ComponentModel
Imports System.ComponentModel.DataAnnotations

Public Class Parser
    Inherits BaseModel

    <Required(ErrorMessage:="Name is required.")>
    Public Property Name As String
        Get
            Return GetValue(Function() Name)
        End Get
        Set(value As String)
            SetValue(Function() Name, value)
        End Set
    End Property

    <Required(ErrorMessage:="Description is required.")>
    Public Property Description As String
        Get
            Return GetValue(Function() Description)
        End Get
        Set(value As String)
            SetValue(Function() Description, value)
        End Set
    End Property

    Public Property InputHeaderInfo As InputHeader
        Get
            Return GetValue(Function() InputHeaderInfo)
        End Get
        Set(value As InputHeader)
            SetValue(Function() InputHeaderInfo, value)
        End Set
    End Property

    Public Property InputVariables As ObservableList(Of Variable)
        Get
            Return GetValue(Function() InputVariables)
        End Get
        Set(value As ObservableList(Of Variable))
            SetValue(Function() InputVariables, value)
        End Set
    End Property

    Public Property OutputVariables As ObservableList(Of Variable)
        Get
            Return GetValue(Function() OutputVariables)
        End Get
        Set(value As ObservableList(Of Variable))
            SetValue(Function() OutputVariables, value)
        End Set
    End Property

    Public Sub New()
        Name = "New Parser"
        Description = "This is a new parser."
        InputHeaderInfo = New InputHeader()
        InputVariables = New ObservableList(Of Variable)
        OutputVariables = New ObservableList(Of Variable)
    End Sub
End Class

ViewModel ->

Imports WinTransform.DataModel
Imports System.IO
Imports WPFHelper
Imports System.ComponentModel
Imports System.ComponentModel.DataAnnotations
Imports ModellingHelper
Imports Omu.ValueInjecter

Namespace ViewModels
    Public Class ParserViewModel
        Inherits ViewBase

#Region "Properties"
        Public Property Source As Parser
            Get
                Return GetValue(Function() Source)
            End Get
            Set(value As Parser)
                SetValue(Function() Source, value)
            End Set
        End Property

        Public Property InputFile As FileInfo
            Get
                Return GetValue(Function() InputFile)
            End Get
            Set(value As FileInfo)
                SetValue(Function() InputFile, value)
                NotifyPropertyChanged(Function() InputFileContents)
                NotifyPropertyChanged(Function() InputFileParseLine)
                NotifyPropertyChanged(Function() TabVisability)
            End Set
        End Property

        Public ReadOnly Property InputFileContents As String
            Get
                If Not InputFile Is Nothing Then
                    Dim mReader = InputFile.OpenText()
                    Try
                        Return mReader.ReadToEnd()
                    Catch ex As Exception
                        MessageBox.Show(String.Format("Failed to load transform file contents: {0}", ex.Message))
                    Finally
                        mReader.Close()
                    End Try
                End If
                Return String.Empty
            End Get
        End Property

        Public ReadOnly Property InputFileParseLine As String
            Get
                If Not InputFile Is Nothing Then
                    Dim mReader = InputFile.OpenText()
                    Try
                        Dim mLines = mReader.ReadToEnd().Split(vbNewLine).Select(Function(l As String) l.Trim())
                        Dim mLineNo = Source.InputHeaderInfo.TitleLinesFixed + Source.InputHeaderInfo.TitleLinesSkipped + Source.InputHeaderInfo.ColumnHeaderLines + Source.InputHeaderInfo.LinesFixed + Source.InputHeaderInfo.LinesSkipped
                        If mLineNo >= 0 And mLineNo < mLines.Count() Then
                            Return mLines(mLineNo)
                        End If
                    Catch ex As Exception
                        MessageBox.Show(String.Format("Failed to load transform file contents: {0}", ex.Message))
                    Finally
                        mReader.Close()
                    End Try
                End If
                Return String.Empty
            End Get
        End Property

        Public ReadOnly Property TabVisability As Visibility
            Get
                If Not InputFile Is Nothing Then
                    Return Visibility.Visible
                End If
                Return Visibility.Hidden
            End Get
        End Property

        Public ReadOnly Property InputVariablesViews As ObservableList(Of VariableViewModel)
            Get
                Dim mVars As New ObservableList(Of VariableViewModel)
                For Each mVar In Source.InputVariables
                    mVars.Add(New VariableViewModel(mVar))
                Next
                AddHandler mVars.CollectionChanged, Sub() Source.InputVariables.RefreshList(mVars.Select(Function(v As VariableViewModel) v.Source))
                Return mVars
            End Get
        End Property

        Public ReadOnly Property OutputVariablesViews As ObservableList(Of VariableViewModel)
            Get
                Dim mVars As New ObservableList(Of VariableViewModel)
                For Each mVar In Source.OutputVariables
                    mVars.Add(New VariableViewModel(mVar))
                Next
                AddHandler mVars.CollectionChanged, Sub() Source.OutputVariables.RefreshList(mVars.Select(Function(v As VariableViewModel) v.Source))
                Return mVars
            End Get
        End Property

        Public Property IsSaved As Boolean
            Get
                If String.IsNullOrEmpty(SaveFile) Then
                    Return False
                End If

                If Not IsValid Then
                    Return False
                End If

                Return GetValue(Function() IsSaved)
            End Get
            Set(value As Boolean)
                SetValue(Function() IsSaved, value)
            End Set
        End Property

        Public Property SaveFile As String
            Get
                Return GetValue(Function() SaveFile)
            End Get
            Set(value As String)
                SetValue(Function() SaveFile, value)
            End Set
        End Property
#End Region

#Region "Commands"
        Public ReadOnly Property SelectInputFile As ICommand
            Get
                Return New RelayCommand(Sub() SelectInputFileExecute())
            End Get
        End Property

        Private Sub SelectInputFileExecute()
            Dim mOpenDialog = OpenDialog
            If mOpenDialog.ShowDialog() Then
                InputFile = New FileInfo(mOpenDialog.FileName)
            End If
        End Sub
#End Region

        Public Sub New()
            Source = New Parser()
            Init()
        End Sub

        Public Sub New(ByVal mFileInfo As FileInfo)
            Source = LoadParser(mFileInfo)
            SaveFile = mFileInfo.FullName
            Init()
        End Sub

        Public Sub Init()
            AddHandler PropertyChanged, Sub() IsSaved = False
            AddHandler Source.InputHeaderInfo.PropertyChanged, Sub() NotifyPropertyChanged(Function() InputFileParseLine)
        End Sub

        Public Shared Function LoadParser(ByVal mFileInfo As FileInfo) As Parser
            Try
                Dim xmlParser As New XmlDataModel.Parser()
                xmlParser.FromXmlFile(mFileInfo.FullName)
                Dim baseParser As New Parser()
                baseParser.InjectFrom(New ParserInjectionXml(baseParser, xmlParser), xmlParser)
                Return baseParser
            Catch ex As Exception
                MessageBox.Show(String.Format("Could not open parser: {0}", ex.Message))
                Return New Parser()
            End Try
        End Function

        Public Sub Save()
            If String.IsNullOrEmpty(SaveFile) Then
                Dim mSaveDialog = SaveDialog
                If mSaveDialog.ShowDialog() Then
                    SaveFile = mSaveDialog.FileName
                Else
                    Return
                End If
            End If
            IsSaved = Save(SaveFile)
        End Sub

        Public Function Save(ByVal mFilePath As String) As Boolean
            SaveFile = mFilePath
            Return SaveParser(mFilePath, Source)
        End Function

        Public Shared Function SaveParser(ByVal mFilePath As String, ByVal mParser As Parser) As Boolean
            If Not mParser.IsValid Then
                Return False
            End If

            Try
                Dim xmlParser As New XmlDataModel.Parser()
                xmlParser.InjectFrom(New ParserInjectionXml(mParser, xmlParser), mParser)
                xmlParser.ToXmlFile(mFilePath)
                Return True
            Catch ex As Exception
                MessageBox.Show(String.Format("Could not save parser: {0}", ex.Message))
                Return False
            End Try
        End Function
    End Class
End Namespace

What I am wondering is, if there is a better way to structure the view model to improve data binding, so I don't have to bind to Source.Name etc. How should I handle the base model in the view model?

Thanks, Alex.

It really depends.

If your Model already implements INotifyPropertyChanged and uses collection types that implement INotifyCollectionChanged, I personally feel that directly encapsulating and binding to "Source.Name" in XAML has some real advantages - mainly, it dramatically reduces the amount of code, and it (more importantly) reduces the amount of unnecessary code duplication.

However, Model classes often aren't designed specifically with WPF or Silverlight support in mind, and require wrapping. As soon as you have to wrap parts of the Model into the ViewModel for handling specific notifications, wrapping the entire model leads to more consistency.

This really has a cost-benefit trade-off to consider. If you're working with a different designer, wrapping everything leads to consistency in the API (that the designer uses), which can help reduce bugs (at the cost of some extra duplication on your part). If you're doing everything, then it's really up to you what makes the most sense.

WPF: How to make a Google Chrome style GUI in WPF/MVVM?

5 votes

I bet you guys know Google Chrome browser, yeah it comes from Chromium open-source projects, added some Google specific features.

I found the multi-tab GUI is quite attractive, especially the "drag-and-drop" feature for tabs and windows:

  • to "pull a tab" out to form a separate window, or
  • vice versa, to join a tab into a windows (that has a collection of tabs).

This would be quite helpful for designing some multi-process applications to achieve the stability, and a cool user experience, but ... how?

  1. is it possible to do this in WPF?
  2. or even one step more, is it possible to do this in MVVM?

Yeah, technically, everything is possible, but I can't see an easy pattern to do this

  1. for WPF, how to handle such specific "crossing window" mouse interaction?
  2. for MVVM, hmmm, will this be too challenging for MVVM?

cheers

Its definitely possible. I've recreated the chrome tab look in the past with MVVM, complete with drag and drop, and I know another guy who created a tabbed application that lets you "pull away" tabs into a new window of its own. I believe it was all one application with multiple windows, so all open windows were part of the same application even if they show up separately in the task bar.

I'm not sure if I have his source code or not, I'd have to go digging for it.

Limit attached dependency property in wpf

5 votes

I want to attach a dependency property to specific controls only.

If that is just one type, I can do this:

public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.RegisterAttached("MyProperty", typeof(object), typeof(ThisStaticWrapperClass));

public static object GetMyProperty(MyControl control)
{
    if (control == null) { throw new ArgumentNullException("control"); }

    return control.GetValue(MyPropertyProperty);
}

public static void SetMyProperty(MyControl control, object value)
{
    if (control == null) { throw new ArgumentNullException("control"); }

    control.SetValue(MyPropertyProperty, value);
}

(So: limit the Control type in the Get/Set-Methods)

But now I want to allow that property to get attached on a different type of Control, too.
You'd try to add an overload for both methods with that new type, but that fails to compile because of an "Unknown build error, Ambiguous match found."

So how can I limit my DependencyProperty to a selection of Controls?
(Note: In my specific case I need it for TextBox and ComboBox)

Ambiguous match found.

...is normally thrown by GetMethod if there are multiple overloads and no type-signature has been specified (MSDN: More than one method is found with the specified name.). Basically the WPF-engine is only looking for one such method.

Why not check the type in the method body and throw an InvalidOperationException if it's not allowed?


Note however that those CLR-Wrappers should not include any code beside the setting and getting, if the propery is set in XAML they will be disregarded, try throwing an exception in the setter, it will not come up if you only use XAML to set the value.

Use a callback instead:

public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.RegisterAttached
        (
            "MyProperty",
            typeof(object),
            typeof(ThisStaticWrapperClass),
            new UIPropertyMetadata(null, MyPropertyChanged) // <- This
        );

public static void MyPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    if (o.GetType() != typeof(TextBox) && o.GetType() != typeof(ComboBox))
    {
        throw new InvalidOperationException("This property may only be set on TextBoxes and ComboBoxes.");
    }
}

what is User32.dll and How it is used in WPF?

2 votes

As Title describes, i am trying to find out what is User32.dll and Where/how it is used in WPF and Windows Forms?

User32.dll is a core windows dll used for windowing and other interactive user program tasks. WPF does not use it much.

Windows.Forms is mostly a .net wrapper around User32, and so is still based entirely on GDI,GDI+, and window handles.

WPF is a newer, and largely different framework that is not nearly so dependent on window messages and window handles (hWnd). It draws on the screen using Direct* and is thus more powerful and often performs better by offloading the graphics rendering to the video card.

WPF Background Two Tone

1 votes

I'm trying to do something so simple but cannot get anywhere with it. I've been trawling the net trying to find a clear example or tit bit of information but every article I find is showing the exact same simple example.

All I want to do is have a background to a list box item that is two tone but without a gradial blend between them. So far I have:

<Setter Property="Background" >
    <Setter.Value>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#ACC6E0" Offset="0"/>
            <GradientStop Color="#DCE7F5" Offset="1"/>
        </LinearGradientBrush>
    </Setter.Value>
</Setter>

I've tried plenty of different things but all I end up with is a variant of a gradual gradient not a two tone equal split.

Many Thanks

Paul

Just add two stops at the same offset:

    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="#ACC6E0" Offset="0"/>
        <GradientStop Color="#ACC6E0" Offset="0.5"/>
        <GradientStop Color="#DCE7F5" Offset="0.5"/>
        <GradientStop Color="#DCE7F5" Offset="1"/>
    </LinearGradientBrush>

In fact you can drop the end points:

    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="#ACC6E0" Offset="0.5"/>
        <GradientStop Color="#DCE7F5" Offset="0.5"/>
    </LinearGradientBrush>

Dock Panel Dock right and left

1 votes

I know a only little about dock panel, following is the code is used:

        <DockPanel LastChildFill="True" >
            <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" VerticalAlignment="Center" Margin="10" >
                <StackPanel Orientation="Horizontal" DockPanel.Dock="Left" VerticalAlignment="Center" >
                    <TextBlock Height="24" Name="Welcome" Text="Welcome" FontSize="14" />
                </StackPanel>
                <StackPanel Orientation="Horizontal" DockPanel.Dock="Right" VerticalAlignment="Center" >
                    <TextBlock Height="24" Name="Welcomee" Text="Welcomee" FontSize="14" />
                </StackPanel>
            </StackPanel>
        </DockPanel>

The result is like this:

WelcomeWelcomee

However, the code must result in something like this:

Welcome                                                              Welcomee

So could you please tell me where I misunderstood the concept.

Your first StackPanel should not contain the other two stack panels. It contains the other two. DockPanel.Dock="left" only applies to the immediate children of the DockPanel.

   <DockPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" VerticalAlignment="Center" Margin="10" >Top
        </StackPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Left" VerticalAlignment="Center" >
                <TextBlock Height="24" Name="Welcome" Text="Welcome" FontSize="14" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Right" VerticalAlignment="Center" >
               <TextBlock Height="24" Name="Welcomee" Text="Welcomee" FontSize="14" />
        </StackPanel>
    </DockPanel>

Remove Table from HTML or XAML C#

1 votes

I have used a HTML to XAML (FlowDocument) conversion script, the only problem is that I don't want tables to be displayed.

I would prefer using the HTML code for this example:

<table>
<tr>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>

OK just for examples sake, that is the formatting of the table, as it is. I would like to replace the tags with something maybe like a <p> or <br /> how would I go about doing this?

Thanks

In very simplistic terms:

string html = "...";
html = html.Replace("<table>","<p>");
html = html.Replace("<td>","");
html = html.Replace("</td>"," ");
html = html.Replace("<tr>","");

html = html.Replace("</tr>","<br/>");
html = html.Replace("</table>","</p>");

You'll get an extra space after your closed in case you have more than one . You can account for this through more thorough logic.

WPF Data Binding and Templates

0 votes

I am working on a project and I want to have a list box with a custom data template populate with user data. My question is, when I click on an item in the list box, how can I tell what item I selected? Basically, if I select "kevin", I want to display his data. If I select Dave, I want to display his data. I do not know how to get the data our after it is bound...

EDIT: I found a really great tutorial that covers this. A very hidden gem.

http://msdn.microsoft.com/en-us/library/aa480224.aspx

Bind SelectedItem of the ComboBox to any property.

<Window x:Class="ComboBoxSelectedItemBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="500">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <ListBox   x:Name="st"
              ItemsSource="{Binding Path=Customers,Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" 
              SelectedItem="{Binding Path=SelectedCustomer,Mode=TwoWay}"
               Margin="0,38,0,80">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Path=Name}"></TextBlock>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <TextBlock Text="{Binding Path=SelectedCustomer.Name}" Grid.Column="1" VerticalAlignment="Center" Margin="5"></TextBlock>
</Grid>

    public partial class Window1 : Window, INotifyPropertyChanged
{
    private ObservableCollection<Customer> customers;
    public ObservableCollection<Customer> Customers
    {
        get
        {
            return customers; 
        }
        set
        {
            customers = value;
            NotifyPropertyChanged("Customers");
        }
    }

    private Customer selectedCustomer;
    public Customer SelectedCustomer
    {
        get
        {
            return selectedCustomer;
        }
        set
        {
            selectedCustomer = value;
            NotifyPropertyChanged("SelectedCustomer");
        }
    }


    public Window1()
    {
        Customers = new ObservableCollection<Customer>();
        Customers.Add(new Customer() { ID = 1, Name = "Ravi", Salary = 1000 });
        Customers.Add(new Customer() { ID = 99, Name = "Alex", Salary = 3000 });
        Customers.Add(new Customer() { ID = 123, Name = "Steve", Salary = 100 });
        Customers.Add(new Customer() { ID = 31, Name = "Alice", Salary = null });
        InitializeComponent();

        DataContext = this;
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    #endregion
}

public class Customer:INotifyPropertyChanged
{
    private int id;
    public int ID
    {
        get
        {
            return id;
        }
        set
        {
            id = value;
            NotifyPropertyChanged("ID");
        }
    }

    private string name;
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            NotifyPropertyChanged("Name");
        }
    }

    private decimal? salary;
    public decimal? Salary
    {
        get
        {
            return salary;
        }
        set
        {
            salary = value;
            NotifyPropertyChanged("Salary");
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    #endregion
}