Showing posts with label wpf. Show all posts
Showing posts with label wpf. Show all posts

Thursday, December 8, 2016

WPF and Castle Windsor

Before using Castle Windsor I used to implement a provider keeping static references to view-models and reference these in the View using Xaml Databinding... something like DataContext="{x:static ViewModelProvider.MainViewModel}".

Now after getting to know Castle Windsor and its ability to handle references it makes totally sense to solve that differently (or to be concrete: to solve that better). For development time we still need to hard code a design-time-view-model cause else we loose all the features of the IDE... Nevertheless Windsor should take over the control about the wiring of View and ViewModel. Main reason therefore is the power (features like Interceptors) and the flexibility (DynamicParameters, Load Config from XML, work with interfaces) coming up with.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    public class ActivatorWPFVM<T> : DefaultComponentActivator
    {
        public ActivatorWPFVM(ComponentModel model, IKernelInternal kernel, ComponentInstanceDelegate onCreation, ComponentInstanceDelegate onDestruction)
            : base(model, kernel, onCreation, onDestruction)
        {

        }
        protected override object CreateInstance(CreationContext context, ConstructorCandidate constructor, object[] arguments)
        {
            var component = base.CreateInstance(context, constructor, arguments);
            if(component is Window)
            {
                ((Window)component).DataContext = DIProvider.Container.Resolve<T>();
            }
            return component;
        }
    }

So by calling...

container.Register(
   Component
      .For<MainWindow>() 
      .Activator<ActivatorWPFVM<MainViewModel> >() 
      .LifestyleTransient());

... we can create a Window reference with an already wired up connection to its ViewModel (in config section where it should be).

kr,
Daniel

Friday, June 10, 2016

Dependency Injection with Windsor and WPF

Hi,

this month I started reading about dependency injection and found out that castle.dynamicProxy (already mentioned in earlier posts) works great with castle.windsor. This might not be surprising but nevertheless after my first project using both nuget-packages I can definitely say that using these has changed my way of work. In this project my classes are shorter, better testable and my interfaces are more proper. (About a year ago I read the book about Clean Code Development by Robert C. Martin. The book is an advertisement for TDD with some pointers about coding style and development mind sets).

One thing to mention about my project using castle.windsor: it supports even more separation of Views and ViewModels of WPF. I used a custom Activator of generic type T (derived from DefaultComponentActivator) to activate my window and asked the windsor container to resolve a singleton viewModel of type T which can be set as the datacontext (override of CreateInstance). I stored the container as a static member of a centrally available class.

So:

  • WPF-Activator<ViewModel>
  • Still consider to set the DesignViewModel in the XAML code directly
  • create a DIProvider 
  • use windsor - installers
  • remove startup-uri in app.xaml and start mainwindow with showdialog
  • prefer dependency injection over service locator pattern
  • use interfaces instead of implementations


kr, D

Tuesday, February 23, 2016

Filter DataGrid in C#/WPF using ICollectionView

Today I tried to find a bug in a ICollectionView-based WPF filtering action. The problem was fortunately easy to find, but I was shocked that I didn't find an easy example in our projects to explain my colleague the way how this all works... So, here is my easy example of a grid which can be filtered:

I created a standard WPF project and installed the nuget-package mvvm-light (see: http://www.codeproject.com/Articles/321806/MVVMLight-Hello-World-in-10-Minutes if you don't know the nuget package). Long story short: it provides some base classes and some assets like a relay-command which is useful to start your work without the need of creating boiler-plate code again and again. The mvvmlight's viewmodel-locator is defined as a static resource in app.xaml so you can define view-models (and its instances) centrally using an "inversion of control"-container (called SimpleIoc). Here nothing has to be changed, because it creates a MainViewModel (as an example) we will use to create this sample.
I kept the MainWindow.xaml which was created by default and put the needed controls in there:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<Window x:Class="FilteringGrids.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:FilteringGrids"
        mc:Ignorable="d"
        Title="Filter-Test" Height="350" Width="525"
        DataContext="{Binding Main, Source={StaticResource Locator}}">
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
 
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
 
        <TextBox Grid.Row="0" Text="{Binding FilterText}" Margin="0,0,10,10"></TextBox>
        <Button Grid.Row="0" Grid.Column="1" Margin="0,0,10,10" Height="25" Width="50" Command="{Binding RefreshCommand}">Refresh</Button>
        <Button Grid.Row="0" Grid.Column="2" Margin="0,0,0,10" Height="25" Width="50"  Command="{Binding FilterCommand}">Filter</Button>
        <DataGrid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding Data}" />
    </Grid>
</Window>

As we can see in line 9: the datacontext is wired up with the viewmodel instance created in the ViewModelLocator, so we can use the paradigm of MVVM. In the following XAML code we bind the text to filter, the data and two buttons to the background viewmodel...


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Input;
namespace FilteringGrids.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        public class Model
        {
            public string Name { get; set; }
            public string Value1 { get; set; }
            public string Value2 { get; set; }
            public string Value3 { get; set; }
            public string Value4 { get; set; }
        }
        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            #region Filter
            FilterText = "";
            Predicate<object> filterFunction = (object raw) =>
            {
                Model dataToFilter = raw as Model;
                return
                    dataToFilter.Name.Contains(FilterText) ||
                    dataToFilter.Value1.Contains(FilterText) ||
                    dataToFilter.Value2.Contains(FilterText) ||
                    dataToFilter.Value3.Contains(FilterText) ||
                    dataToFilter.Value4.Contains(FilterText);
            };
            FilterCommand = new RelayCommand(() => DataCollectionView.Refresh(),
                                             () => true);
            #endregion
            #region Init / Refresh
            RefreshCommand = new RelayCommand(() =>
            {
                Data = new List<Model> {
                    new Model() { Name="one",   Value1="1", Value2="1.0", Value3="1.00", Value4="1.000" },
                    new Model() { Name="two",   Value1="2", Value2="2.0", Value3="2.00", Value4="2.000" },
                    new Model() { Name="three", Value1="3", Value2="3.0", Value3="3.00", Value4="3.000" },
                    new Model() { Name="four",  Value1="4", Value2="4.0", Value3="4.00", Value4="4.000" },
                };
                DataCollectionView = CollectionViewSource.GetDefaultView(Data);
                DataCollectionView.Filter = filterFunction;
                this.RaisePropertyChanged("Data");
            }, () => true);
            // init data
            RefreshCommand.Execute(null);
            #endregion
        }
        public string FilterText { get; set; }
        public List<Model> Data { get; set; }
        private ICollectionView DataCollectionView { get; set; }
        public ICommand RefreshCommand { get; set; }
        public ICommand FilterCommand { get; set; }
    }
}


What we see here is that 99% of the magic happens in the constructor of the viewmodel. This is bad style, but keeps the things easy for the example, so please forgive me here. We see here that all members (defined at the end of the class) are bound by the xaml-code except the CollectionView which is used for the actual filtering.
Important here is:

  • if you change the reference of your data source then populate that to the UI (INPC -> RaisePropertyChanged)
  • Filtering over ICollectionView is easily achievable over a Predicate-function
  • recreate the ICollectionView if the original instance of the data changes (you probably don't need a reference to this)
  • if the filter-result might change call CollectionViewInstance.Refresh()

kind regards,
Daniel

Friday, January 9, 2015

WPF Master-Detail sync

today I dived into WPF data binding and checked how to create an easy master-detail view. This following sample show my findings (description below):

XAML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<Window x:Class="MasterDetail.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" 
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="118" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <ListView Grid.Column="0"
                  Grid.Row="0"
                  Margin="5"
                  ItemsSource="{Binding PersonList}" IsSynchronizedWithCurrentItem="True"
                  SelectedIndex="0">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name"
                                    DisplayMemberBinding="{Binding Name}"
                                    Width="Auto" />
                </GridView> 
            </ListView.View>
        </ListView>
        <ListView Grid.Column="1"
                  Grid.Row="0"
                  ItemsSource="{Binding PersonList/OrderList}"
                  IsSynchronizedWithCurrentItem="True"
                  Margin="5">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name"
                                    DisplayMemberBinding="{Binding Name}"
                                    Width="Auto" />
                </GridView>
            </ListView.View>
        </ListView>
        <Button Grid.Column="1"
                Grid.Row="1"
                Width="100"
                Height="25"
                HorizontalAlignment="Right"
                Margin="0,5,5,5">Close</Button>
    </Grid>
</Window>

Code behind:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MasterDetail
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public class Person : INotifyPropertyChanged //,IEditableObject
        {
            #region Name

            private string name;
            public string Name
            {
                get
                {
                    return name;
                }
                set
                {
                    name = value;
                    RaisePropertyChanged();
                }
            }
            
            #endregion

            public ObservableCollection<Order> OrderList { get; set; }
            public Person(string name, Order[] list)
            {
                this.Name = name;
                this.OrderList = new ObservableCollection<Order>();
                list.ToList().ForEach(x => this.OrderList.Add(x));
            }

            public event PropertyChangedEventHandler PropertyChanged;
            private void RaisePropertyChanged([CallerMemberName]string prop = null)
            {
                if(this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
                }
            }
        }

        public class Order
        {
            public string Name { get; set; }
            public Order(string name)
            {
                this.Name = name;
            }
        }

        public ObservableCollection<Person> PersonList { get; set; }
        public MainWindow()
        {
            PersonList = new ObservableCollection<Person>();
            
            #region set data
            
            PersonList.Add(new Person("Peter Parker", 
                           new Order[]{
                                new Order("spiderman costume"),
                                new Order("comics"),
                                new Order("science books")
                           }));

            PersonList.Add(new Person("Tony Stark",
                           new Order[]{
                                new Order("screw driver"),
                                new Order("tie"),
                                new Order("headset"),
                                new Order("Mobile phone")
                           }));

            PersonList.Add(new Person("Bruce Benner",
                           new Order[]{
                               new Order("shorts")
                           }));

            #endregion

            InitializeComponent();
        }
    }
}

Findings:

  • Setting the datacontext to the codebehind is a bit tricky see line 7 in XAML
  • IsSynchronizedWithCurrentItem makes sense if you use more than 1 control to display a collection. The control is not directly using the list, but it wraps it using a "default" listcollectionview, which has a current item. This property can be used to sync the positions of the different controls. A problem here is, that listcollectionview is a wpf class, which should not be used inside the viewmodel so getting the current item can be a bit tricky and possibly needs to be done in a view-service, which returns a reference to the current item.
  • line 32 is the highlight in this post. First I tried the same with a dot ("."), but there the output window of visual studio told me (eligible) that there is no property of the given name for the observablecollection. What does the trick is to use the current item, which will be requested by using the slash ("/"). So binding to /OrderList binds to the order list of the current item. It is different behavior to a collection-view which automatically binds to the current item.
kind regards, Daniel

Thursday, January 8, 2015

WPF Zooming

... in response to MarkLTX, who posted an awesome article about zooming and how double-animation makes the user-experience much better ( http://www.codeproject.com/Tips/860914/Add-Zooming-to-a-WPF-Window-or-User-Control ):

In addition to the article I would like to mention a method which wasn't described. I think it is the most natural way in a WPF application to use XAML-Binding (without any code behind) and to use the slider only, instead of using an animation as e.g.: Word 2013 does it (maybe they also have an animation to keep things smooth, but if so, I don't really recognize it). Anyway, the animation was added to give the user a better feeling about what is going on and to show him (through the animation) that the current action which will be proceed is "zooming" and not any magic which confuses him.

I think that the user-activity of changing a slider makes it unnecessary to do any other things (as long as the performance of the application is OK).

Here a sample application which uses a slider to zoom:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<Window x:Name="window"
        x:Class="ZoomerApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <WrapPanel Margin="10,10,10,0">
            <WrapPanel.LayoutTransform>
                <TransformGroup>
                    <ScaleTransform ScaleX="{Binding Value, ElementName=ZoomSlider}"
                                    ScaleY="{Binding Value, ElementName=ZoomSlider}" />
                    <SkewTransform />
                    <RotateTransform />
                    <TranslateTransform />
                </TransformGroup>
            </WrapPanel.LayoutTransform>
                <Button>Hello</Button>
                <Button>Hello</Button>
                <Button>Hello</Button>
                <Button>Hello</Button>
                <Button>Hello</Button>
                <Button>Hello</Button>
                <Button>Hello</Button>
                <Button>Hello</Button>
                <Button>Hello</Button>

        </WrapPanel>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="1*" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Slider x:Name="ZoomSlider"
                    Width="100"
                    HorizontalAlignment="Right"
                    Margin="10,2,10,0"
                    Grid.Column="0"
                    Value="1" />
            <StackPanel Orientation="Horizontal"
                        Grid.Column="1">
                <Label Content="{Binding Value, ElementName=ZoomSlider}"
                       ContentStringFormat="{}Zooming: {0:N2}" />
            </StackPanel>
            <Button Width="100"
                    Content="OK"
                    Margin="20,2,0,10"
                    Height="25"
                    Grid.Column="3"
                    IsDefault="True" />
            <Button Width="100"
                    Margin="2,2,10,10"
                    Content="Cancel"
                    Height="25"
                    Grid.Column="4"
                    IsCancel="True" />
        </Grid>
    </Grid>
</Window>


What I did:
  • I created a window with a main grid and 2 containers inside...
    • the top container is a WrapPanel which will be zoomed
    • the bottom container has a slider which sets the zoom level
  • The binding is on the slider.value (so I named the slider ZoomSlider to be able to call it by its element-name). There are much more elegant ways, but I think this one is elegant enough for this quick sample.
  • line 18 and 19 are the most important
    • a binding is set up on the value of the slider
    • the value is set on line 49 to default 1 what equals 100%
  • line 52 creates a label which visualizes the content of the slider.value.
I used to create zooming like this and always got good feedback about it (I think the main reason is because it behaves so similar to the word-solution). What also makes this solution attractive for the developer is that no code in the code-behind is needed to accomplish zooming (as long as the zooming level should not be persisted).

kind regards, Daniel

Tuesday, October 7, 2014

design data in wpf (best practise)

I used to have a DB connection in my wpf applications which will be set from outside so that the application has no idea what the connection string could be. The result is, that I start my application development without any design data.

What is nice here is that you can check whether you are in design mode or not and set the connection string. Therefore check:

http://msdn.microsoft.com/en-us/library/system.componentmodel.designerproperties.getisindesignmode.aspx

... but in fact, I really don't like that solution because it works only if you have wpf references in the project what a DB-access layer typically doesn't have. Pushing the connection string from the ui-layer to the business logic layer and then to the dbaccess-layer for design makes it a mess (spaghetti code). Moreover you need a database connection even at design time to work on the ui, what can be painful too.

I found my solution inside the following msdn blog post with some additional brain work:

http://blogs.msdn.com/b/wpfsldesigner/archive/2010/06/30/sample-data-in-the-wpf-and-silverlight-designer.aspx

I would recommend in a MVVM pattern UI to set the datacontext to the VM and set d:DesignData right afterwards to a child class of that class.

so e.g.: a grid's data context is bound to a class called something like MainWindowViewModel and d:DataContext (as described at the msdn article) is bound to something like MainWindowViewModelDesignData.

The ...DesignData should inherit from the base view model and initialize the properties with hard coded design data values (or maybe xml files which are always available on hard drive). I would recommend to set extreme values (min/max values) inside the view model or even better prepare more than one vm-designdata class (or what I would do: put in a switch which can be modified quickly), because it is much easier to change the desing data, compile, close and open the window than to compile, start, prepare test data, check, prepare other test data, check,... and so on. The design data is giving you a perfect overview how the ui would look like and is a perfect solution to test your ui about extreme values entered by the user (e.g.: a login name can have 7 to 50 characters: make cases with a) only 7 char logins, b) only 50 char logins and c) a mix with even 25 char logins).

This worked fine for me. Please let me know if you see it differently.

kr, daniel

EDIT:

today (about a month later) I used the mvvm light framework which is shipped with a dependency injection service, where view-models are registered. With mvvm light it is even easier to register design data in design mode.

Service-Locator code:


1
2
3
4
5
6
7
8
            if (ViewModelBase.IsInDesignModeStatic)
            {
                SimpleIoc.Default.Register<MainViewModel, MainDesignViewModel>();
            }
            else
            {
                SimpleIoc.Default.Register<MainViewModel>();
            }


kind regards, Daniel

Wednesday, May 28, 2014

Column Panel

Hi,

today I wanted to arrange my elements in a grid in another way than stackpanel does. So I started to search for panel-implementations and found a simple implementation for a radial panel. Unfortunately a radial panel was not the solution for my problem so I had to build it myself. To tell the truth: it is even easier than I thought.

Panel
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace ColumnPanelProject
{
    public class ColumnPanel : Panel
    {
        #region ColumnsCount

        public int ColumnsCount
        {
            get { return (int)GetValue(ColumnsCountProperty); }
            set { SetValue(ColumnsCountProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ColumnsCount.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColumnsCountProperty =
            DependencyProperty.Register("ColumnsCount", typeof(int), typeof(ColumnPanel), new PropertyMetadata(0));

        #endregion

        private readonly Size InfinitySize = new Size(double.PositiveInfinity, double.PositiveInfinity);
        
        protected override Size MeasureOverride(Size availableSize)
        {
            
            foreach (UIElement child in Children)
            {
                child.Measure(InfinitySize);
            }

            return base.MeasureOverride(availableSize);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            if(this.Children.Count > 0)
            {            
                int columns = this.ColumnsCount != 0 ? 
                                this.ColumnsCount : 
                                (int)(finalSize.Width / Children[0].DesiredSize.Width) ;

                int x = 0;
                int y = 0;

                foreach (UIElement child in Children)
                {
                    child.Arrange(new Rect( x * child.DesiredSize.Width,
                                            y * child.DesiredSize.Height, 
                                            child.DesiredSize.Width, 
                                            child.DesiredSize.Height));

                    x = (x + 1) % columns;
                    y = y + (x == 0 ? 1 : 0);
                }
            }

            return finalSize;
        }
    }
}

Window
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<Window x:Class="ColumnPanelProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ColumnPanelProject"
        xmlns:System="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="456.579" Width="481.579"
        >
    <Window.Resources>
        <System:Int32 x:Key="columnCount">0</System:Int32>
        <ItemsPanelTemplate x:Key="columnTemplate">
            <local:ColumnPanel ColumnsCount="{DynamicResource columnCount}" />
        </ItemsPanelTemplate>

        <DataTemplate x:Key="dataTemplate">
            <Grid Margin="3" Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Border BorderThickness="5" BorderBrush="LightGray" CornerRadius="10">
                    <TextBlock TextWrapping="Wrap" Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
                </Border>
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>

        <ItemsControl
            Grid.Column="0"
            Grid.Row="0"
            Grid.ColumnSpan="2"
            ItemsSource="{x:Static Member=local:MainWindow.MyItems}"
            Name="itemsControl" 
            ItemTemplate="{DynamicResource dataTemplate}" 
            ItemsPanel="{DynamicResource columnTemplate}" Margin="0,2,0,-2" 
            />
        <Label Grid.Row="1" Grid.Column="1" Content="{DynamicResource columnCount}" HorizontalContentAlignment="Center" />
    </Grid>
</Window>

Code-Behind
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ColumnPanelProject
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static ObservableCollection<string> myItems = null;

        public static ObservableCollection<string> MyItems
        {
            get
            {
                if(myItems == null)
                {
                    myItems = new ObservableCollection<string>();
                    (new[]{
                        "Dr. Leonard Leakey Hofstadter",
                        "Dr. Dr. Sheldon Lee Cooper",
                        "Penny",
                        "Howard Joel Wolowitz",
                        "Dr. Rajesh „Raj“ Ramayan Koothrappali",
                        "Dr. Bernadette Maryann Rostenkowski-Wolowitz",
                        "Dr. Amy Farrah Fowler",
                        "Stuart Bloom"}).ToList().ForEach(x => myItems.Add(x));
                }

                return myItems;
            }
        }

        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

This article should show how a grid arrangement can be set up using only MeasureOverride / ArrangeOverride.

referring to msdn MeasureOverride should implement:
  • Iterate your element's particular collection of children that are part of layout, call Measure on each child element.
  • Immediately get DesiredSize on the child (this is set as a property after Measure is called).
  • Compute the net desired size of the parent based upon the measurement of the child elements.
The return value of MeasureOverride should be the element's own desired size, which then becomes the measure input for the parent element of the current element. This same process continues through the layout system until the root element of the page is reached.


ArrangeOverride is obviously responsible for the arrangement of the items.


A good related article can be found here: http://drwpf.com/blog/2009/08/05/itemscontrol-n-is-for-natural-user-interface/


Kind regards,
Daniel

Tuesday, May 13, 2014

catch uncaught exceptions c# wpf

Hi,

today I heard about a solution to catch exceptions globally in C# with WPF:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public App()
        {
            DispatcherUnhandledException += App_DispatcherUnhandledException;
        }

        void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            MessageBox.Show("error");
            e.Handled = true;
            this.Shutdown(1);
        }
    }

This snippet handles ALL exceptions which are not caught, shows the message "error" and quits with error.

Kind regards,
Daniel