Best wpf questions in December 2011

How to track ONE person with Kinect (trackingID)

7 votes

I would like to track the first person, and use this person's right hand to navigate in the application that I made.

I can take over the cursor, now I just want only one person being tracked. So basically when one person is navigating in the program, and there are people walking behind him or are looking with this guy, if they move, the kinect shouldn't recognise anyone else.

How can I implement this, I know it's something with the trackingId but what? :s

        foreach (SkeletonData s in allSkeletons.Skeletons)
        {

                if (s.TrackingState == SkeletonTrackingState.Tracked)
                {
                    if (s.TrackingID == 0)
                    {

                        foreach (Joint joint in s.Joints)
                        {
                        }
                    }
                }
        }

Every tracked person has a player index. Just ignore players with other indexes.
The player index is part of the data in the depth stream image. You have to extract it:

int playerIdx = depthFrame16[i16] & 0x07;

In order to get this info you have to initialize your Kinect Runtime correctly:

_kinectNui.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | ....

See here for more infos: http://www.codeproject.com/KB/dotnet/KinectGettingStarted.aspx

I totally recommend this video tutorial from MS: http://research.microsoft.com/apps/video/?id=152249

If you look in the ShapeGameDemo that is coming with the SDK you can see how they do it. (They just use the index of the skeletion in the array):

int playerId = 0;
foreach (SkeletonData data in skeletonFrame.Skeletons) {
   if (SkeletonTrackingState.Tracked == data.TrackingState) {
      Player player;
      if (players.ContainsKey(playerId))
         player = players[playerId];
      else
         player = new Player(playerId);
   }
   playerId++;
}

Simplifying things you can do that (using your code):

int myPlayerIndex = 0; //probably 0 since you are the first person entered the kinect scope
int playerId = 0;
foreach (SkeletonData s in allSkeletons.Skeletons) {
   if(playerId != myPlayerIndex)
      continue;       

   if (s.TrackingState == SkeletonTrackingState.Tracked) {
      foreach (Joint joint in s.Joints)
      {
      }
   }
   playerId++;
}

To round things up here is a similar question in an MS forum that explains it: http://social.msdn.microsoft.com/Forums/en-US/kinectsdk/thread/d821df8d-39ca-44e3-81e7-c907d94acfca

WPF One Way Binding broken

7 votes

Im trying to bind 2 different WPF controls to the same property in the ViewModel, a CheckBox.IsChecked and an Expander.IsExpanded. The behavior I want to achieve is to have the CheckBox affect the ViewModel (and therefore the Expander as well), but not the other way bound. Something like:

Checkbox Checked -> ViewModel property set to frue -> Expander.Expand
Checkbox Unchecked -> ViewModel property set to false -> Expander.Collapse
Expander Expanded -> Nothing else affected
Expander Collapsed -> Nothing else affected

Here's the XAML:

<Window x:Class="WpfApplication9.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">
    <Expander IsExpanded="{Binding IsChecked, Mode=OneWay}">
        <Expander.Header>
            <CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked"/>
        </Expander.Header>
        <TextBlock Text="Expanded!"/>
    </Expander>
</Window>

and the Code:

using System.ComponentModel;
using System.Windows;

namespace WpfApplication9
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }

    public class ViewModel: INotifyPropertyChanged
    {
        private bool _isChecked;
        public bool IsChecked
        {
            get { return _isChecked; }
            set
            {
                _isChecked = value;
                NotifyPropertyChange("IsChecked");
            }
        }

        protected void NotifyPropertyChange(string PropertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    }
}

Now my problem is, as soon as I click on the Expander to expand / collapse it, the Binding seems to stop working. Can anyone explain to me why this is happening and how do I achieve this? Thanks in advance!

New Answer

Discovered you could do this by setting your UpdateSourceTrigger to Explicit on your Expander. This keeps the binding as Two-Way, but never updates the Source since you're telling it not to update the source unless you explicitly tell it to.

<Expander IsExpanded="{Binding IsChecked, UpdateSourceTrigger=Explicit}">
    <Expander.Header>
        <CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked"/>
    </Expander.Header>
    <TextBlock Text="Expanded!"/>
</Expander>

Leaving my old answer below so the comments make sense, and because I still feel there is no problem with view-specific code going in the code-behind of a view :)


Old Answer

Personally since this is View-Specific code, I see no problem with using a CheckBox click event to set the Expander's IsExpanded value.

private void MyCheckBox_Click(object sender, RoutedEventArgs e)
{
    MyExpander.IsExpanded = ((CheckBox)sender).IsChecked.GetValueOrDefault();
}

You could make this even more generic by removing the names and navigating the Visual Tree to find the Expander associated with the CheckBox. Here's an example using some Visual Tree Helpers I built

private void CheckBox_Click(object sender, RoutedEventArgs e)
{
    var chk = (CheckBox)sender;
    var expander = VisualTreeHelpers.FindAncestor<Expander>(chk);

    if (expander != null)
        expander.IsExpanded = chk.IsChecked.GetValueOrDefault();
}

How to create a Textbox and Label with sloped border?

6 votes

I am trying do create a Textbox in Wpf that has a Label in its top left corner and addiotionally this label shall have a border with a slope on one side.

http://imgur.com/Nupbf The way I tried it

Now for one or two specific cases that was doable with a workaround where i just used lines for the border. Now that i want to use it a bit more I need to do it the right way, especially in a way where it is scalable.

I would be really happy if someone could point me in the right direction.

Edit: So the code I am using after taking into account the responses so far, which i created as a user control:

<Grid Height="93" Width="335">
<TextBox TextWrapping="Wrap" Text="{Binding TextboxText}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" BorderBrush="{x:Null}" Background="{x:Null}"/>
<Path Data="M384,242 L442.5,242" HorizontalAlignment="Left" Height="1" Margin="0,28.667,0,0" Stretch="Fill" VerticalAlignment="Top" Width="59.5">
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#8EFFFFFF"/>
<GradientStop Color="White" Offset="0.991"/>
</LinearGradientBrush>
</Path.Fill>
<Path.Stroke>
<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
<LinearGradientBrush.RelativeTransform>
<TransformGroup>                                        <ScaleTransform CenterY="0.5" CenterX="0.5"/>                                   <SkewTransform CenterY="0.5" CenterX="0.5"/>                                <RotateTransform Angle="90" CenterY="0.5" CenterX="0.5"/>                       <TranslateTransform/>
</TransformGroup>
</LinearGradientBrush.RelativeTransform>
<GradientStop Color="White" Offset="0.009"/>
<GradientStop Color="#5FFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Path.Stroke>
</Path>
<Label Content="{Binding LabelText}" HorizontalAlignment="Left" Width="113" FontSize="16" Height="40" VerticalAlignment="Top" BorderBrush="White" Margin="0,0.167,0,0"/>
<Path Data="M125.12574,28.672087 L145.37561,-1.1668457" HorizontalAlignment="Left" Height="30.839" Margin="58.125,-1,0,0" Stretch="Fill" VerticalAlignment="Top" Width="21.25">
<Path.Stroke>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#51FFFFFF" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Path.Stroke>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#49FFFFFF" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
<Path Data="M0,83 L181.35815,83" Fill="#FFF4F4F5" Height="1" Stretch="Fill" VerticalAlignment="Bottom" Width="327" StrokeThickness="2" Margin="0,0,10,10">
<Path.Stroke>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Path.Stroke>
</Path>
</Grid>

It works and the only thing that is still bothering me is the resizability of the label border, which is quite annoying to do but luckily not necessary in my case.

This is another solution which uses a Style instead of a UserControl.

I assume the Label is the description of the TextBox, inside the style, I created a TextBlock (replacing the Label as it would be an overkill in this case) of which Text is bound to the Tag of parent TextBox. Then it will display whatever you put in your Tag.

Also I have grouped TextBlock and two Paths into a Grid, set its columns to be auto-sized so you won't have the resizability issue anymore.

The screenshot below is two TextBoxes with different labels. enter image description here

The TextBox Style

<Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBox}">
    <Setter Property="Background" Value="Black" />
    <Setter Property="Foreground" Value="#FFB8B8B8" />
    <Setter Property="BorderBrush" Value="#FF484848" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="Padding" Value="1" />
    <Setter Property="AllowDrop" Value="true" />
    <Setter Property="FocusVisualStyle" Value="{x:Null}" />
    <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
    <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <Border x:Name="BottomLine" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0,0,0,1">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <Grid x:Name="TopPanel" HorizontalAlignment="Left">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <TextBlock d:LayoutOverrides="Width, Height" x:Name="Caption" TextWrapping="Wrap" Text="{TemplateBinding Tag}" FontSize="14.667" VerticalAlignment="Center" Margin="4,0,24,0" />
                            <Path x:Name="BottomPath" Data="M384,242 L442.5,242" Stretch="Fill" VerticalAlignment="Bottom" Margin="0,0,-1.3,0">
                                <Path.Stroke>
                                    <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
                                        <LinearGradientBrush.RelativeTransform>
                                            <TransformGroup>
                                                <ScaleTransform CenterY="0.5" CenterX="0.5" />
                                                <SkewTransform CenterY="0.5" CenterX="0.5" />
                                                <RotateTransform Angle="90" CenterY="0.5" CenterX="0.5" />
                                                <TranslateTransform />
                                            </TransformGroup>
                                        </LinearGradientBrush.RelativeTransform>
                                        <GradientStop Color="White" Offset="0.009" />
                                        <GradientStop Color="#5FFFFFFF" Offset="1" />
                                    </LinearGradientBrush>
                                </Path.Stroke>
                            </Path>
                            <Path x:Name="RightPath" Data="M125.12574,28.672087 L145.37561,-1.1668457" Stretch="Fill" Grid.Column="1">
                                <Path.Stroke>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="#51FFFFFF" Offset="0" />
                                        <GradientStop Color="White" Offset="1" />
                                    </LinearGradientBrush>
                                </Path.Stroke>
                            </Path>
                        </Grid>
                        <Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true" Margin="0,0,0,-0.001" d:LayoutOverrides="Width, Height" Grid.Row="1">
                            <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        </Microsoft_Windows_Themes:ListBoxChrome>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The TextBoxes

    <TextBox Tag="Label" Text="This is a textbox" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center" FontSize="24" Style="{DynamicResource MyTextBoxStyle}"/>
    <TextBox Tag="Long Label" Text="This is a textbox" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Bottom" FontSize="24" Style="{DynamicResource MyTextBoxStyle}"/> 

Hope this helps. :)

Stop WPF ScrollViewer automatically scrolling to perceived content

6 votes

The Application

I am building an application which includes a range selector. This consists of two custom drawn Slider controls contained within one UserControl derived class. The range selector control is then contained inside a ScrollViewer which has the HorizonalScrollBar visible most of the time.

Sample Application Code: ( appologies for the wall of text )

Window.xaml ( the Window file ):

<Grid>
    <ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Visible"  VerticalScrollBarVisibility="Disabled">
            <local:SliderTest x:Name="slider"                                                                         
                           LowerValue="0"
                           UpperValue="10"
                           Minimum="0"
                           Maximum="100" Width="900" Height="165" Padding="15,0,15,0" HorizontalAlignment="Left">
            </local:SliderTest>
    </ScrollViewer>
</Grid>

SliderTest.xaml:

<UserControl x:Class="scrollviewerDemoProblem.SliderTest"
             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" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             x:Name="root"
             xmlns:local="clr-namespace:scrollviewerDemoProblem"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <ControlTemplate x:Key="simpleSlider" TargetType="{x:Type Slider}">
            <Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Track x:Name="PART_Track" Grid.Row="1">
                        <Track.Thumb>
                            <Thumb x:Name="Thumb" FlowDirection="LeftToRight" Width="15">
                                <Thumb.Template>
                                    <ControlTemplate TargetType="Thumb">
                                        <Canvas>
                                            <Path x:Name="test1" StrokeThickness="0" Fill="DarkGreen">
                                                <Path.Data>
                                                    <GeometryGroup FillRule="NonZero">
                                                        <PathGeometry>
                                                            <PathGeometry.Figures>
                                                                <PathFigure IsClosed="True" StartPoint="0,150" IsFilled="True">
                                                                    <PathFigure.Segments>
                                                                        <PathSegmentCollection>
                                                                            <LineSegment Point="-15,150" />
                                                                            <LineSegment Point="-15,0" />
                                                                            <LineSegment Point="0,0" />
                                                                        </PathSegmentCollection>
                                                                    </PathFigure.Segments>
                                                                </PathFigure>
                                                            </PathGeometry.Figures>
                                                        </PathGeometry>
                                                    </GeometryGroup>
                                                </Path.Data>
                                            </Path>
                                        </Canvas>
                                    </ControlTemplate>
                                </Thumb.Template>
                            </Thumb>
                        </Track.Thumb>
                    </Track>
                </Grid>
            </Border>
        </ControlTemplate>

        <ControlTemplate x:Key="simpleSliderRight" TargetType="{x:Type Slider}">
            <Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Track x:Name="PART_Track" Grid.Row="1">
                        <Track.Thumb>
                            <Thumb x:Name="Thumb" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Width="15">
                                <Thumb.Template>
                                    <ControlTemplate TargetType="Thumb">
                                        <Canvas>
                                            <Path Stroke="Black" StrokeThickness="0" Fill="DarkCyan">
                                                <Path.Data>
                                                    <GeometryGroup FillRule="NonZero">
                                                        <PathGeometry>
                                                            <PathGeometry.Figures>
                                                                <PathFigure IsClosed="True" StartPoint="0,150">
                                                                    <PathFigure.Segments>
                                                                        <PathSegmentCollection>
                                                                            <LineSegment Point="15,150" />
                                                                            <LineSegment Point="15,0" />
                                                                            <LineSegment Point="0,0" />
                                                                        </PathSegmentCollection>
                                                                    </PathFigure.Segments>
                                                                </PathFigure>
                                                            </PathGeometry.Figures>
                                                        </PathGeometry>
                                                    </GeometryGroup>
                                                </Path.Data>
                                            </Path>
                                        </Canvas>
                                    </ControlTemplate>
                                </Thumb.Template>
                            </Thumb>
                        </Track.Thumb>
                    </Track>
                </Grid>
            </Border>
        </ControlTemplate>
    </UserControl.Resources>

    <Grid x:Name="Gridd" VerticalAlignment="Top" Height="165" >
        <Border x:Name="timeScaleBorder" Width="auto" Height="15" VerticalAlignment="Top" Background="Black">
            <Canvas x:Name="timeCanvas" Width="auto" Height="15">
            </Canvas>
        </Border>
        <Border x:Name="background" BorderThickness="1,1,1,1" BorderBrush="Black" VerticalAlignment="Center" Height="150"
                Margin="0,15,0,0" Background="White" />
        <Slider  x:Name="LowerSlider"
                Minimum="{Binding ElementName=root, Path=Minimum}"
                Maximum="{Binding ElementName=root, Path=Maximum}"
                Value="{Binding ElementName=root, Path=LowerValue, Mode=TwoWay}"
                Template="{StaticResource simpleSlider}"
                Margin="0,15,0,0" />
        <Slider  x:Name="UpperSlider"
                Minimum="{Binding ElementName=root, Path=Minimum}"
                Maximum="{Binding ElementName=root, Path=Maximum}"
                Value="{Binding ElementName=root, Path=UpperValue, Mode=TwoWay}"
                Template="{StaticResource simpleSliderRight}"
                Margin="0,15,0,0" />
    </Grid>
</UserControl>

SliderText.xaml.cs:

public partial class SliderTest : UserControl
{
    public SliderTest()
    {
        InitializeComponent();
    }

    #region Dependency properties, values etc.

    public static readonly DependencyProperty MinimumProperty =
        DependencyProperty.Register("Minimum", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));

    public double LowerValue
    {
        get { return (double)GetValue(LowerValueProperty); }
        set { SetValue(LowerValueProperty, value); }
    }

    public static readonly DependencyProperty LowerValueProperty =
        DependencyProperty.Register("LowerValue", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));

    public double UpperValue
    {
        get { return (double)GetValue(UpperValueProperty); }
        set { SetValue(UpperValueProperty, value); }
    }

    public static readonly DependencyProperty UpperValueProperty =
        DependencyProperty.Register("UpperValue", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));

    public double Maximum
    {
        get { return (double)GetValue(MaximumProperty); }
        set { SetValue(MaximumProperty, value); }
    }

    public static readonly DependencyProperty MaximumProperty =
        DependencyProperty.Register("Maximum", typeof(double), typeof(SliderTest), new UIPropertyMetadata(1d));

    public double Minimum
    {
        get { return (double)GetValue(MinimumProperty); }
        set { SetValue(MinimumProperty, value); }
    }
    #endregion        
}

The Problem Most of the sample code provided is boring and the mechanics of it works pretty good. The problem I am having is a visual problem specifically with the ScrollViewer control that I have in the main Window. The ScrollViewer seems to be automatically adjusting the horizontal offset of the ScrollViewer when either of the Slider's gains focus ( from a mouse click for example ).

Reproducing the behaviour

  1. Run the application, you will see that the horizontal scroll bar of the ScrollViewer is visible.
  2. Click on the Green ( far left ) Slider, you will notice that the ScrollViewer automatically adjusts to shift the horizontal offset to where the perceived 'content' starts.

These symptoms occur at either end of the scroll pane.

Screenshot of application when it is run ( Application is Zoomed in 200% for detail clarity ):

Screenshot1

Screenshot of the behavior when the left slider is clicked:

enter image description here

What I want to happen:

When I click on either slider item ( at either end ) when a slider looks to be beyond end of the slider ( slider range is denoted by the black bar at the top ) I don't want the ScrollViewer to automatically adjust it's horizontal offset.

Suspected problem:

I suspect that the problem is that the ScrollViewer perceives the actual 'content' of it's childen starts 15 pixels ( the drawn width of both of my sliders ) in from where the actual drawn content does start. The Canvas only draws because I included a padding of 15 pixels inside of the SliderTest control on the main window, if this padding is removed the ScrollViewer does not show any of the Slider's Canvas.

EDIT : it appears the padding is not the problem, read the comments as to why.

Things I have tried

I have tried looking into overriding the OnPreviewMouseDown event of the main Window. The problem here is that I still want both Slider's to behave normally, setting the event to Handled causes the Slider to stop working completely.

Notes:

The Slider's within the range selector control ( Called SliderTest in this example ) must both have a width of 1 pixel. The slider's must be able to extend 15 pixels past the end of the time selection range ( see the black bar at the top for a reference ).

Thank you for reading this novel-lengthed problem.

By default when a control receives the logical focus, FrameworkElement calls its own BringIntoView method (from within its OnGotFocus method if it has keyboard focus). That results in a RequestBringIntoView event being generated that bubbles up the element tree to allow ancestor elements to bring that portion of the element into view. The ScrollViewer listens for this event and eventually will call MakeVisible on the associated IScrollInfo/ScrollContentPresenter which leaves it up to the panel to bring that portion into view (since the panel would know how it arranges its children). It then takes that returned rect it receives back and asks for that portion of itself to be brought into view (in case you had nested elements that would require some action to ensure the original element was brought into view). So one way to suppress this behavior would be to handle the RequestBringIntoView event on the sliders and mark the event handled.

How can I wait for my async operations to finish when the application gets exited using?

6 votes

If a user performs an operation, such as deleting items, it removes them from the UI right away and then deletes them from the database on a background thread using TPL. The problem is if the user exits the application before the background thread finishes, the item never actually gets deleted.

Is there a standard way of waiting for async operations to finish before shutting down the application?

My async calls look like this:

if (MyObjectList.Contains(obj)) MyObjectList.Remove(obj);
Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));

Update

Here's the final code I went with. I'm quite happy to see it works as it should, although let me know if I can improve it. I still have a lot to learn :)

public partial class App : Application
{
    private List<Task> _backgroundTasks = new List<Task>();

    public App()
    {
        EventSystem.Subscribe<TaskStartedMessage>((e) =>
        {
            _backgroundTasks.Add(e.Task);
        });

        EventSystem.Subscribe<TaskEndedMessage>((e) =>
        {
            if (_backgroundTasks.Contains(e.Task))
                _backgroundTasks.Remove(e.Task);
        });
    }

    protected override void OnExit(ExitEventArgs e)
    {
        Task.WaitAll(_backgroundTasks.Where(p => !p.IsCompleted).ToArray(), 30000);

        base.OnExit(e);
    }
}

And when starting an important background task, I'm using this syntax:

var task = Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));
EventSystem.Publish<TaskStartedMessage>(new TaskStartedMessage(task));
await task;
EventSystem.Publish<TaskEndedMessage>(new TaskEndedMessage(task));

I'm using AsyncCTP for await/async, and Microsoft Prism's EventAggregator for the event system.

There is no standard way but since you create a specific Task here it should be easy to put that in a List and build some Exit-logic to Wait for all Tasks in that List.

OK, a sample. Untested and incomplete:

// untested
static class CriticalTasks
{
    static HashSet<Task> tasks = new HashSet<Task>();
    static object locker = new object();

    // when starting a Task
    public static void Add(Task t)
    {
        lock(locker)
           tasks.Add(t);
    }

    // When a Tasks completes
    public static void Remove(Task t)
    {
        lock(locker)
           tasks.Remove(t);
    }

    // Having to call Remove() is not so convenient, this is a blunt solution. 
    // call it regularly
    public static void Cleanup()
    {
        lock(locker)
           tasks.RemoveWhere(t => t.Status != TaskStatus.Running);
    }

    // from Application.Exit() or similar. 
    public static void WaitOnExit()
    {
        // filter, I'm not sure if Wait() on a canceled|completed Task would be OK
        var waitfor = tasks.Where(t => t.Status == TaskStatus.Running).ToArray();
        Task.WaitAll(waitfor, 5000);
    }
}


The drawback is that you will have to extend each Task with the code to Add & Remove it.

Forgetting a Remove() (eg when an Exception happens) would be a (small) memory-leak. It is not too critical, instead of burdening your code with using() blocks you could also periodically run a Cleanup() method that uses HashSet.RemoveWhere() to remove non-running tasks.

Is it possible to run a Windows app within a console window?

5 votes

I have a Windows app that I've written in C# 4 and WPF; I've now been asked if I can add a commandline parameter (e.g. /console) that would force it to run as a console app so it can be run by a task scheduler.

Is this possible with modern apps? Or do I need to create a separate console app?

UPDATE: can I just emphasise that this is a WPF application. There is no convenient static void Main(string[] args) entry point to hook into. But the PM would still like the app to have the ability to run from the commandline...

FINAL UPDATE: the trick, as pointed out by @RodH257, is that the WPF app codegens the expected static void Main. You can add your own class with a method of the same name and in the project build properties, set it as the startup object for the executable. You'll also need the [STAThread] attribute on the method so that WPF will run properly too.

You can either turn it into a console application and manually show the first WPF dialog. just as you would if you were creating a DLL and starting a WPF window, as per below

 static class Program
{
    static void Main(params string[] args)
    {
        if (args.Length > 0)
        {
           //do stuff here
            return;
        }
          Window1 window = new Window1();
          window.ShowDialog();
    }
}

OR, have a look at this link for editing the entrypoint on your current WPF project and act based on arguments How to write custom Main method for a WPF application?

EDIT: Updated the post to make it more clear for the exact situation described.

MVVM property depends on a graph of objects

5 votes

I am working with WPF+MVVM.

I have a VM which contains a Customer property. The Customer has an ObservableCollection of Orders. Each Order has an ObservableCollection of Items. Each Items has a Price.

Now, I have the following property on my VM:

public double TotalPrice
{
    return Customer.Orders.Sum(x => x.Items.Sum(y => y.Price));
}

The problem is whenever a change occurs at any point in this graph of objects - the UI should be notified that TotalPrice had changed - but it doesn't...

For example if the Customer will be altered from A to B, or an order will be added, or an item will be deleted, or an item's price will be altered etc.

Does anyone has an elegant solution for this?

Thanks.

You can find here an interesting post written by me few days ago and it talks exactly about this problem (and its solution...)

MVVM design feels too unwieldy, am I doing it wrong?

5 votes

I've recently started creating a WPF application, and am just hoping that someone can confirm to me that I'm building my overall system architecture properly, or correct me if I'm heading off in the wrong direction somewhere. Especially since I'm trying to do MVVM, there are a lot of layers involved, and I'm not sure I'm doing things properly.

Here's a simplified description of the system:

Data is stored in an SQL Server database, which is accessed through Linq to SQL. Let's say that the database contains two tables, USERS and USER_GROUPS. Each table has an auto-generated Linq to SQL class, DB_USER and DB_USER_GROUP.

Now in the application, I want to display a ListBox with each ListBoxItem containing various UI elements for displaying/modifying the users' info, which is done using a DataTemplate.

I have a view model class for the window, which uses a Linq to SQL query (joining the two tables) to populate an ObservableCollection<User> named UserList, which the ListBox in the window has bound as its ItemsSource. User is a class implementing INotifyPropertyChanged that handles all the formatting/getting/setting of database data into what's needed by the WPF controls. The section of code handling this is something like:

DBDataContext db = new DBDataContext();

var allUsers = from user in db.USERs
                   .Where(u => u.ENABLED == true)
               from group in db.USER_GROUPs
                   .Where(g => g.GROUPID == u.GROUPID)
                   .DefaultIfEmpty()
               select new { user, group };

foreach (var user in allUsers)
{
    User u = new User(db, user.user, user.group);
    UserList.Add(u);
}

So the User class is constructed with private properties for a DB_USER, a DB_USER_GROUP, and the database DataContext class. All of a User's public properties basically wrap the relevant columns, with their get methods returning the values for WPF to use, and set changing the column(s) and then calling SubmitChanges() on the private DataContext property to update the database.

This is all working fine, but it feels a little unwieldy, so I'm just wondering if I've missed something that would make it cleaner. Specifically, storing a DataContext inside each element of UserList seems odd, but I wasn't sure of a better method to be able to update the database whenever data was changed in the UI.

Any feedback is appreciated, and please let me know if anything's unclear, I'm not sure how well I've explained it.

Starting off, let's put some labels on what you are doing here: DB_USER is your Model and User is your ViewModel (I 'd have preferred UserViewModel for the latter just so that it's more clear what's going on).

One thing that's immediately obvious is that it's not really proper for your ViewModel to have functionality suited to your Model, i.e. that DataContext does not belong where it currently is. This is a piece of information that should either be in your Model, or alternatively encapsulated in some DataStore/DataService (take your pick) class. Your ViewModel would then be responsible, when the time comes to save any changes, to tell the DataStore "here's an updated snapshot of this model, please save it for me" (this would most likely be exposed to the UI through an ICommand). This feels cleaner and underscores the idea that your ViewModel is a layer that adapts the realities of your model to your choice of UI.

Other than the above, there's nothing in what you describe that I feel needs to be "corrected". However, I can offer some suggestions regarding things that you have not elaborated on.

Exposing data from a Model through a ViewModel is always something that can be implemented in many ways. When considering what approach to take, you should take into account the possibility of the same Model being exposed through different Views at the same time. In this case, IMHO the preferred approach is to have a separate ViewModel for each View (the Views may well be of different types, so they could have different expectations from the ViewModel adapter thus pointing to multiple types of ViewModels as well), so you would need to use a pattern that allows changes to be communicated from one ViewModel to any others in "real time".

One way to do this would be to make your Models implement INotifyPropertyChanged themselves and have each ViewModel hook into its Model for notifications, so when a change occurs ViewModel A pushes the change to the Model, and the Model notifies ViewModel B.

However personally I don't like polluting my Models with what is in essence code that only caters to the needs of the UI, so another approach is needed. That would be making the DataService I mentioned above expose functionality (methods and events) through which ViewModel A can tell the service "hey, the Model I 'm wrapping has had some changes" ; note that this is different from "I want you to persist the current snapshot of this Model". ViewModel B has already hooked into a suitable "ModelChanged" event, so it gets notified and pulls the updated information from the service. This has the added benefit that if at any time the service detects that the backing data repository has been updated by a source external to the current process, there's a ready made mechanism to broadcast a "Calling all ViewModels: Model X has been updated, any interested parties please talk to me about learning the details" message.

Above all, always keep in mind that there is no "one true MVVM style" and there are myriads of possible approaches. Which one to take depends not only on hard facts and the current position of the slider on the YAGNI/HyperEngineering scale, but also on, dare I say, your taste.

DispatcherTimer not firing in WPF Application

5 votes

I am trying to understand why the DispatcherTimer contained within SingletonWithTimer is not firing in the following WPF application. I've been researching this for a couple of days and cannot seem to get to the bottom it. This application is the reduced essential parts of an existing application that I'm trying to fix. The Startup object of this project is WPFApplication5TimerTest.Program.

The output in the console lists as follows, the problem is evident because the word "TimerTick" is not shown in the output:

Timer is initialized
'WpfApplication5TimerTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework.Aero\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Sample thread
Sample thread
Sample thread
Sample thread
Sample thread
Sample thread
The thread '<No Name>' (0x10b0) has exited with code 0 (0x0).
Sample thread exiting!

This is Program.cs:

using System;

namespace WpfApplication5TimerTest
{
    static class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            AppObject = new App();
            AppObject.Run();
        }

        public static App AppObject
        {
            get;
            private set;
        }
    }
}

This is App.xaml.cs:

using System;
using System.Threading;
using System.Windows;

namespace WpfApplication5TimerTest
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            var sampleThread = new Thread(new ThreadStart(SampleThreadEntryPoint));            
            sampleThread.Start();
            new MainWindow().Show();
        }

        private void SampleThreadEntryPoint()
        {
            SingletonWithTimer.Initialize();
            while (!_shutdownEvent.WaitOne(1000))
                Console.WriteLine("Sample thread");
            Console.WriteLine("Sample thread exiting!");
        }

        protected override void OnExit(ExitEventArgs e)
        {
            _shutdownEvent.Set();
        }

        private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);        
    }
}

This is MainWindow.xaml.cs:

using System;
using System.Windows;

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

        private void Window_Closed(object sender, EventArgs e)
        {
            Program.AppObject.Shutdown();
        }
    }
}

This is SingletonWithTimer.cs:

using System;
using System.Windows.Threading;

namespace WpfApplication5TimerTest
{
    public class SingletonWithTimer
    {
        private static SingletonWithTimer Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new SingletonWithTimer();
                }
                return _instance;
            }
        }

        public static void Initialize()
        {
            SingletonWithTimer.Instance._timer = new DispatcherTimer();
            SingletonWithTimer.Instance._timer.Interval = TimeSpan.FromSeconds(2);
            SingletonWithTimer.Instance._timer.Tick += new EventHandler(SingletonWithTimer.Instance.OnTimerTick);
            SingletonWithTimer.Instance._timer.Start();
            Console.WriteLine("Timer is initialized");
        }

        private void OnTimerTick(object sender, EventArgs e)
        {
            Console.WriteLine("TimerTick");
        }

        private static SingletonWithTimer _instance;
        private DispatcherTimer _timer = null;
    }
}

It's because you've created the DispatcherTimer on another (non-UI) thread. Hence, it will be tied to the Dispatcher on that thread, not the one on the UI thread. Since nothing is running the Dispatcher on that thread, it will never fire.

Either create the DispatcherTimer on the UI thread, or use a constructor overload that allows you to pass in a specific Dispatcher to use.

I've started a process, how do I call a method in that process

5 votes

My VB6 macro (COM) has successfully called into my managed code, COM-visible stub. My COM-visible stub has successfully started my WPF process (.exe). "Life is good". Now, I need to access a method within my WPF process and pass in some parameters.

I know that I can start my WPF process with parameters, but my VB6 macro will occasionally call my COM-visible stub with new parameters and I need to pass this into my running process.

I've thought of stopping/re-starting my process with new parameters, but that seems somewhat extreme.

How do I access a method withing my running process?

From the operating system's point of view, a process doesn't have methods. So the only way to communicate between processes is to use some kind of inter-process communication. In C#, you could using a Remoting IPC channel or WCF. Take a look here.

Is there a WPF equivalent to a DOM explorer?

5 votes

When I'm drawing a layout on a webpage, using CSS, there can be dozens of rules, scattered across dozens of files, that could possibly influence how an element is actually displayed. Which is why the DOM explorers are such critical tools - I can select an element on a browser and see exactly what CSS rules are being applied to it.

In WPF, there can again be many rules - styles and templates and inline attributes and settings injected from the code-behind - that could possibly be interacting to determine how a given element is displayed.

Is there a way for me to look at an element, say a ComboBox, and to quickly determine exactly why it is drawing three times as tall as I think it should?

Personally, I use Snoop. I'm not familiar with Mole, but I heard it is "better" (subjective), though it isn't free. There is a trick to working with Snoop though, if you have a window created that isn't the main window, you can target it by doing a Ctrl+Shift+MouseOver (that actually targets the current visual control). This took me awhile to figure out, and knowing that is super useful!

Binding with StringFormat on a custom control

5 votes

I'm trying to use a custom control in a WPF app, and I have some problem using a StringFormat binding.

The problem is easy to reproduce. First, let's create a WPF application and call it "TemplateBindingTest". There, add a custom ViewModel with only one property (Text), and assign it to the DataContext of the Window. Set the Text property to "Hello World!".

Now, add a custom control to the solution. The custom control is as simple as it can get:

using System.Windows;
using System.Windows.Controls;

namespace TemplateBindingTest
{
    public class CustomControl : Control
    {
        static CustomControl()
        {
            TextProperty = DependencyProperty.Register(
                "Text",
                typeof(object),
                typeof(CustomControl),
                new FrameworkPropertyMetadata(null));

            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
        }

        public static DependencyProperty TextProperty;

        public object Text
        {
            get
            {
                return this.GetValue(TextProperty);
            }

            set
            {
                SetValue(TextProperty, value);
            }
        }
    }
}

When adding the custom control to the solution, Visual Studio automatically created a Themes folder, with a generic.xaml file. Let's put the default style for the control in there:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TemplateBindingTest">

    <Style TargetType="{x:Type local:CustomControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomControl}">
                    <TextBlock Text="{TemplateBinding Text}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Now, just add the control to the window, and set a binding on the Text property, using a StringFormat. Also add a simple TextBlock to be sure that the binding syntax is correct:

<Window x:Class="TemplateBindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:TemplateBindingTest="clr-namespace:TemplateBindingTest" Title="MainWindow" Height="350" Width="525">
<StackPanel>
    <TemplateBindingTest:CustomControl Text="{Binding Path=Text, StringFormat=Test1: {0}}"/>
    <TextBlock Text="{Binding Path=Text, StringFormat=Test2: {0}}" />
</StackPanel>

Compile, run, aaaaand... The text displayed on the window is:

Hello World!

Test2: Hello World!

On the custom control, the StringFormat is completely ignored. No error is visible on VS output window. What's going on?

Edit: The workaround.

Ok, the TemplateBinding was misleading. I found the cause and a dirty workaround.

First, notice that the problem is the same with the Content property of Button:

<Button Content="{Binding Path=Text, StringFormat=Test3: {0}}" />

So, what's going on? Let's use Reflector and dive to the StringFormat property of the BindingBase class. The 'Analyse' feature shows that this property is used by the internal DetermineEffectiveStringFormat method. Let's see this method:

internal void DetermineEffectiveStringFormat()
{
    Type propertyType = this.TargetProperty.PropertyType;
    if (propertyType == typeof(string))
    {
         // Do some checks then assign the _effectiveStringFormat field
    }
}

The problem is right here. The effectiveStringFormat field is the one used when resolving the Binding. And this field is assigned only if the DependencyProperty is of type String (mine is, as Button's Content property, Object).

Why Object? Because my custom control is a bit more complex than the one I pasted, and like the Button I want the control's user to be able to provide child controls rather than just text.

So, what now? We're running into a behaviour existing even in WPF core controls, so I can just leave it "as is". Still, as my custom control is used only on an internal project, and I want it to be easier to use from XAML, I decided to use this hack:

using System.Windows;
using System.Windows.Controls;

namespace TemplateBindingTest
{
    public class CustomControl : Control
    {
        static CustomControl()
        {
            TextProperty = DependencyProperty.Register(
                "Text",
                typeof(string),
                typeof(CustomControl),
                new FrameworkPropertyMetadata(null, Callback));

            HeaderProperty = DependencyProperty.Register(
                "Header",
                typeof(object),
                typeof(CustomControl),
                new FrameworkPropertyMetadata(null));

            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
        }

        static void Callback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            obj.SetValue(HeaderProperty, e.NewValue);
        }

        public static DependencyProperty TextProperty;
        public static DependencyProperty HeaderProperty;

        public object Header
        {
            get
            {
                return this.GetValue(HeaderProperty);
            }

            set
            {
                SetValue(HeaderProperty, value);
            }
        }

        public string Text
        {
            set
            {
                SetValue(TextProperty, value);
            }
        }
    }
}

Header is the property used in my TemplateBinding. When a value is provided to Text, the StringFormat is applied since the property is of type String, then the value is forwarded to the Header property using a callback. It works, but it's really dirty:

  • The Header and the Text property aren't in sync, as Text isn't updated when I update Header. I choose to provide no getter to the Text property to avoid some mistakes, but it can still happen if someone directly reads the value from the DependencyProperty (GetValue(TextProperty)).
  • Unpredictable behaviour may happen if someone provides a value to both Header and Text property as one of the values will be lost.

So overall, I wouldn't recommand using this hack. Do it only if you are really in control of your project. If the control has even the slightest chance of being used on another project, just give up on the StringFormat.

StringFormat is used when binding to a string property, while the Text property in your control is of type object, hence StringFormat is ignored.