Skip navigation

In my NHibernate core classes I decided to have more informative ToString method, and so I’ve done something like that:

public override string ToString()
{
  StringBuilder sb = new StringBuilder();
  var props = TypeDescriptor.GetProperties(this);
  foreach (PropertyDescriptor p in props)
  {
      sb.AppendFormat("{0} : {1} \n", p.Name, p.GetValue(this));
  }
  return sb.ToString();
}

Very simple, isn’t it? … But it is wrong!

The code above results in Stackoverflow exception. You will not find any information on that in search engine as certain very popular site has such name ;-) and so all find results points there..

Ok so my new ToString():

public override string ToString()
{
  StringBuilder sb = new StringBuilder();
  sb.Append(base.ToString());

  var props = GetType().GetProperties()
      .Where(p => Attribute.IsDefined(p, typeof(DomainSignatureAttribute), true) &&
      (!p.PropertyType.IsSubclassOf(typeof(IEnumerable))) &&
      (!p.PropertyType.IsSubclassOf(typeof(BaseObject))));

  if (props.Count() > 0)
  {
      sb.AppendLine();
  }

  foreach (PropertyInfo p in props)
  {
        sb.AppendFormat("{0} : {1} \n", p.Name, p.GetValue(this, null));
  }
  return sb.ToString();
}

As I use a concept of DomainSignatureAttribue (from Sharp Architecture) and so I decided to show only those properties in ToString() method. I filter collections and other entities that my class refer out, as well, so there should be no circular code references any more.

And I’ve get rid of stack overflow :-) .

First look at sample window:
Adornertest
The adorner’s right border is shift a little, well it does not looks nice.
Any ideas?

The solution is quite simple … the window is too small to contain adorner, as the TextBox inside fulfills the windows width, and so the solution is quite simple: I need just to add some margins, that’s it.

I’ve mostly operate on well known types: int, string and DateTime, and I’ve newer suspect that some hidden dragon is there… And so once I’ve decided to be more specific, we’ve got ushorts and other beautiful types..
And there is my waring: BE VERY CONSISTENT. If your property is ushort your mapping must be type UInt16, if you map it as Int16 you will end with Invalid Cast, and with no precise clue which property is guilty!
So again my piece of advice: if you mean ushort map UInt16, not anything else.

I’ve always tried to work with as many super new, hot libraries as possible, and so I’ve put myself into a trap.
One of my projects used: NHibernate, NHibernate addins, Fluent NHibernate, NHibernate Validator and Castle Windsor obviously.
Four NHibernateXXX projects in one and all compiled from their trunks, as you can imagine it was quite funny to synchronize all common necessary libraries. But as time passed it wast not so funny, so I decided to some limitation. So I’ve get rid of Fluent NHibernate. And guess what: nothing terrible happed, quite contrary now I don’t have any problems with consistency as all the rest is updated in their trunks by their (great) teams. And additionally configuring NHibernate in xml is not a problem at all, as intellisense works good, and it is good documented. (What I miss is conventions from Fluent NH, but now I can be super up-to-date) :-)

Hi,
When dealing with UI validation functions I’ve found that I need to get a good looking, descriptive name for my UI elements: textboxes, comboboxes etc.. and so if that elements have labels with target property so the rest is quite simple:

        private string GetLabelTextForControl(DependencyObject targetElement, DependencyObject searchIn)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(searchIn); i++)
            {
                DependencyObject v = VisualTreeHelper.GetChild(searchIn, i);
                Label childVisual = v as Label;
                if (childVisual != null)
                {
                    if (childVisual.Target == targetElement)
                    {
                        return childVisual.Content.ToString();
                    }
                }
                else
                {
                    if (VisualTreeHelper.GetChildrenCount(v) > 0)
                    {
                        string respValue = GetLabelTextForControl(targetElement, v);
                        if (!string.IsNullOrEmpty(respValue))
                        {
                            return respValue;
                        }
                    }
                }
            }

            return string.Empty;
        }

It’s quick and dirty but it works. The parameters are: the control which name you are searching and the panel, user control or window in which you are searching.
It works if you have Target property set e.g.:

        <Label Target="{Binding ElementName=txtBox}" Content="{Binding Path=Caption, ElementName=me}" />
        <TextBox x:Name="txtBox" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" TextWrapping="Wrap"/>

So it’s just a quick answer to a question: How to get labels for UI Controls in WPF, but I’d gladly invite cleaner solution.

Well it is nothing super creative, but it took me a while to do that.
And so you have probably two possible solutions: write your own implementation of IConfiguratorProvider, or as I did configure the default one:

var nhConfigurator = new DefaultSessionFactoryConfigurationProvider();
nhConfigurator.AfterConfigure += nhConfigurator_AfterConfigure;
var sfp = new SessionFactoryProvider(nhConfigurator);
            
container.Register(Component
                               .For<ISessionFactoryProvider>()
                               .Named("sessionFactoryProvider")
                               .Instance(sfp));

static void nhConfigurator_AfterConfigure(object sender, ConfigurationEventArgs e)
{
     NHConfigurationBuilder.AddMappingAssembliesTo(e.Configuration,  new string[] { "XXXX.Dal.dll" });
}

And that’s it. The ConfigurationEventArgs expose configuration object as you can see. That event could also be used to export db schema, as it is done in uNHaddins examples.
Additionally I’ve found useful information in Gustavo Ringel blog. And of course in HunabKu blog.

We should have our work get easier, shouldn’t we?
So I don’t like to repeat label + text box all over, that duet is a good choose for user control, and so it is:
(and keep remember, I’m still learning..)

PlainTextBox.xaml:

<UserControl x:Class="PlainTextBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="me" 
    Style="{StaticResource MyControlStyle}">
    <UserControl.Resources>
        <ResourceDictionary>
            <Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip"
                            Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                            Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
        </ResourceDictionary>
    </UserControl.Resources>
    <StackPanel>
        <Label Target="{Binding ElementName=txtBox}" Content="{Binding Path=Caption, ElementName=me}" />
        <TextBox x:Name="txtBox"/>
    </StackPanel>
</UserControl>

PlainTextBox.xaml.cs

   public partial class PlainTextBox : UserControl
    {
        // Using a DependencyProperty as the backing store for Caption.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CaptionProperty =
            DependencyProperty.Register("Caption", typeof(string), typeof(PlainTextBox), new UIPropertyMetadata(""));

        // Using a DependencyProperty as the backing store for Path.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PathProperty =
            DependencyProperty.Register("PathName",
                typeof(string),
                typeof(PlainTextBox),
                new PropertyMetadata("", MyPathChanged));

        public PlainTextBox()
        {
            InitializeComponent();
        }

        public string Caption
        {
            get { return (string)GetValue(CaptionProperty); }
            set { SetValue(CaptionProperty, value); }
        }

        public string PathName
        {
            get { return (string)GetValue(PathProperty); }
            set { SetValue(PathProperty, value); }
        }


        private static void MyPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PlainTextBox tc = d as PlainTextBox;
            Binding myBinding = new Binding("MyDataProperty");
            myBinding.Mode = BindingMode.TwoWay;
            myBinding.Path = new PropertyPath(tc.PathName);
            myBinding.ValidatesOnDataErrors = true;
            myBinding.ValidatesOnExceptions = true;
            tc.txtBox.SetBinding(TextBox.TextProperty, myBinding);
        }
    }

And usage:

 <ctr:PlainTextBox Caption="Last name" PathName="DetailData.LastName"></ctr:PlainTextBox>

And that way all data binding attributes are encapsulated in user control, so I do not need repeat that I want validation on errors all over in my code.

In previous post I’ve used Pager class which is contained in my grid controller, and so there is an implementation:

    [NotifyPropertyChanged]
    public class Pager : IPager
    {
        private uint m_CurrentPage;

        private uint m_PageSize;

        private uint m_RecordFrom;

        private uint m_RecordTo;

        public Pager()
        {
            m_PageSize = 50;
            PageNumber = 0;
        }

        public uint PageNumber
        {
            get
            {
                return m_CurrentPage;
            }
            set
            {
                uint from = m_PageSize * value;
                RecordFrom = from;
                RecordTo = from + m_PageSize  - 1;
                m_CurrentPage = value;
            }
        }

        public uint PageSize
        {
            get
            {
                return m_PageSize;
            }
            set
            {
                m_PageSize = value;
                PageNumber = 0;
            }
        }

        public uint RecordFrom
        {
            get
            {
                return m_RecordFrom;
            }
            private set
            {
                m_RecordFrom = value;
                RecordFromDisplay = value + 1;
            }
        }

        public uint RecordFromDisplay { get; private set; }

        public uint RecordTo
        {
            get
            {
                return m_RecordTo;
            }
            private set
            {
                m_RecordTo = value;
                RecordToDisplay = value + 1;
            }
        }

         public uint RecordToDisplay { get; private set; }

        public event EventHandler PagesChanged;

        public bool CanPreviousPage()
        {
            return PageNumber > 0;
        }

        public void NextPage()
        {
            PageNumber++;
            OnPagesChanged();
        }

        [Preview("CanPreviousPage")]
        [Dependencies("PageNumber")]
        public void PreviousPage()
        {
            if (CanPreviousPage())
            {
                PageNumber--;
                OnPagesChanged();
            }
        }

        private void OnPagesChanged()
        {
            if (PagesChanged != null)
            {
                PagesChanged(this, EventArgs.Empty);
            }
        }
    }

and the view:

<UserControl x:Class="GridToolbar"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ca="clr-namespace:Caliburn.Actions;assembly=Caliburn.Actions"
    xmlns:cm="clr-namespace:Caliburn.RoutedUIMessaging;assembly=Caliburn.RoutedUIMessaging"
    >
    <Action.Target>
        <Binding />
    </Action.Target>

    <UserControl.Resources>
        <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="MainWindowResources.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    <DockPanel LastChildFill="True">
        <ToolBar DockPanel.Dock="Top">
            <Button ca:Action.Target="{Binding Pager}" cm:Message.Attach="PreviousPage">
                <Image Source="..\Resources\Images\arrow_180.png" Opacity="1" Stretch="None" VerticalAlignment="Center" />
            </Button>
            <Label Content="{Binding Pager.RecordFromDisplay}"></Label>
            <Label>...</Label>
            <Label Content="{Binding Pager.RecordToDisplay}"></Label>
            <Button ca:Action.Target="{Binding Pager}" cm:Message.Attach="NextPage">
                <Image Source="..\Resources\Images\arrow.png" Opacity="1" Stretch="None" VerticalAlignment="Center" />
            </Button>
            <Separator></Separator>
            <Button cm:Message.Attach="OnShowDetail">
                <Image Source="..\Resources\Images\document_horizontal_text.png" Opacity="1" Stretch="None" VerticalAlignment="Center" />
            </Button>
            <Button cm:Message.Attach="LoadData">
                <Image Source="..\Resources\Images\arrow_circle_double.png" Opacity="1" Stretch="None" VerticalAlignment="Center" />
            </Button>
            <Button cm:Message.Attach="OnGoToFilter">
                <Image Source="..\Resources\Images\binocular.png" Opacity="1" Stretch="None" VerticalAlignment="Center" />
            </Button>
        </ToolBar>
    </DockPanel>
</UserControl>

Please notice how caliburn simplified the code! Because the DataContext is set to a grid controller certain actions could be bound to it directly, like OnGoToFilter, but buttons for paging was to be bound to the Pager class, so additionally I had to indicate the action target. Clean and simple.

And how it looks like:

wpftoolbar

It is without any particular styling. The left one of course, the right one is from my winforms project. And don’t complain on blury text and images, MS will fix it soon (I hope so).

As the best way to learn something is to play with it, so I build a little mvvm data system. Actually I’ve got similar with winforms (MVP with passive views), so I try to add another UI to the system.

A common data from contains three components:

  • filter
  • grid view and
  • detail view (of one record)

Because grid view is the same for all data objects (with the difference of the columns), I decided to keep it as a one user control and only configure columns.

And so is my solution:

GridController.cs:

    [NotifyPropertyChanged]
    public abstract class GridController : ViewModelBase, IGridController, IGridConfiguator
    {
        private IDictionary<string, object> m_Criteria;

        protected Type m_CurrentType;

        private IPager m_Pager;

        public bool AllowMultiselect { get; set; }

        public object CurrentItem { get; set; }

        public IMultidataRepository DataRepository { get; set; }

        /// <summary>
        /// Gets or sets the grid data. Data can be aded by that property, or by ShowData function
        /// </summary>
        /// <value>The grid data.</value>
        public IList GridData { get; protected set; }

        public IPager Pager
        {
            get
            {
                return m_Pager;
            }
            set
            {
                m_Pager = value;
                m_Pager.PagesChanged += Pager_PagesChanged;
            }
        }

        public event EventHandler GoToFilter;

        public event EventHandler ShowDetail;

        /// <summary>
        /// Loads the data. If m_Criteria are null or empty then it should equal as GetAll()
        /// </summary>
        public void LoadData()
        {
            GridData.Clear();
            using (new Workspace())
            {
                var x = DataRepository.FilterByProperties(m_CurrentType, m_Criteria, null, Pager.RecordFrom, Pager.PageSize);
                foreach (var item in x)
                {
                    GridData.Add(item);
                }
            }
        }

        public void OnGoToFilter()
        {
            if (GoToFilter != null)
            {
                GoToFilter(this, EventArgs.Empty);
            }
        }

        public void OnShowDetail()
        {
            if (ShowDetail != null)
            {
                ShowDetail(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Shows the data. Data will be loaded form the storage and displayed in a grid
        /// </summary>
        ///
<param name="criteria">The criteria.</param>
        public void ShowData(IDictionary<string, object> criteria)
        {
            m_Criteria = criteria;
            LoadData();
        }

        private void Pager_PagesChanged(object sender, EventArgs e)
        {
            LoadData();
        }

        public virtual void Configure(DataGrid gridToConfigure)
        {
        }
    }

It is a ModeView class which contains IPager class for switching between pages of records.
NotifyPropertyChanged class attribute is for Postsharp to add required code for properties.
And the GenericView:

    <StackPanel>
        <local:GridToolbar></local:GridToolbar>
        <wt:DataGrid x:Name="GridWithData" ItemsSource="{Binding GridData, Mode=OneWay}" SelectedItem="{Binding CurrentItem}">
        </wt:DataGrid>
    </StackPanel>

Generic grid view contains grid toolbar and a grid wich is common for every data object. And additionally it requires code behind file:

    public partial class GenericGridView : UserControl
    {
        public GenericGridView()
        {
            InitializeComponent();

            Loaded += GenericGridView_Loaded;
        }

        void GenericGridView_Loaded(object sender, RoutedEventArgs e)
        {
            ConfigureGrid();
        }

        private void ConfigureGrid()
        {
            IGridConfiguator c = DataContext as IGridConfiguator;
            if (c != null)
            {
                GridWithData.AutoGenerateColumns = false;
                c.Configure(GridWithData); 
            }
            else
            {
                GridWithData.AutoGenerateColumns = true;
            }
        }
    }

Of course Configure function should be implemented in concrete class eg. PersonGridViewModel:

    [NotifyPropertyChanged]
    public class PersonGridViewModel : GridController 
    {
        public PersonGridViewModel()
        {
            m_CurrentType = typeof(PersonForGrid);
            GridData = new ObservableCollection<PersonForGrid>();
        }

        public override void Configure(DataGrid gridToConfigure)
        {
            gridToConfigure.Columns.Add(
                new DataGridTextColumn()
                {
                    Header = "First name",
                    IsReadOnly = true,
                    Binding = new Binding(PersonForGrid.FIRSTNAME) 
                });
            gridToConfigure.Columns.Add(
                new DataGridTextColumn()
                {
                    Header = "Surename",
                    IsReadOnly = true,
                    Binding = new Binding(PersonForGrid.LASTNAME)
                });
            gridToConfigure.Columns.Add(
                new DataGridTextColumn()
                {
                    Header = "Birth date",
                    IsReadOnly = true,
                    Binding = new Binding(PersonForGrid.BIRTHDATY)
                    {
                        StringFormat = "d"
                    }
                });
        }
    }

Actually that solution fits me, but probably it not the last word :-)

The first is ICommand, why on earth we are obliged to repeat relaying command wrappers? I personally hate to repeat the same code all over. The same thing is with INotifyPropertyChanged.

And my cure for that: caliburn, which basically eliminate need of ICommands all over, and Postsharp, which can help to implement INotifyPropertyChanged. I’ve found that in Postsharp samples directory there is a INotifyPropertyChanged implementation, and is quite decent. (For a project of mine, I’ve just changed the code to intercept also private setters).

And my irritation has decreased.

One thing left: build ui with ioc.

And so my solution is to build view model classes with ioc, and they can expose some “configuration” classes for views. I’ll explain it detailed in next post.

Follow

Get every new post delivered to your Inbox.