יום ראשון, 10 באפריל 2011

WPF - Find Parent Control in VisualTree - Generics Methods

If you need to find parent control, but you would like to use generic method for find one, you can use
method I wrote:


public UIElement GetParent<TSource>(UIElement element, UIElement stopOn)
{
   while (element != null && !(element is TSource) && (element != stopOn))
   {
      element = VisualTreeHelper.GetParent(element) as UIElement;               
   }
   if (element == stopOn)
      return null;
   else
      return element;
}
and you can implement it like this:
UIElement expander = GetParent<Expander>(element, parent);
if ( expander != null)
     return;
P.S.
Its very helpful if you are using routed events of WPF (instance:PreviewMouseLeftButtonDown)
and you want to stop bubble events on one of control on the way.

יום חמישי, 7 באפריל 2011

WPF Data Binding Converter


Following Post explains how to create Converter for WPF data binding.
For example we need to bind Lable control to his Background propertie:


 <Label.Background>
                    <Binding Path="BranchColor">...

but in Data Source we are getting string instead of Color: Red, Blue, Green and so...

We need to create converter that will possible to convert string "colors" to System.Windows.Model class.

We need to perform following steps:



1. Write Converter class with converts and Converts back methods:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Globalization;
using System.Windows.Media;
using System.Drawing;

namespace GarageManager.Converters
{  
    [ValueConversion(typeof(decimal), typeof(string))]
    public class BrushesConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return null;
            BrushConverter convertor = new BrushConverter();
            return convertor.ConvertFromString(value.ToString());
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return null;
            BrushConverter convertor = new BrushConverter();
            return convertor.ConvertToString(value);
        }
    }
}

2. Register it on our XML file where we writing data binding:

xmlns:local="clr-namespace:GarageManager.Converters"

3. and perform data biding with converter:

 <Label x:Name="lblBranchColor"  Grid.Column="0" Grid.RowSpan="3" Width="5">
                <Label.Background>
                    <Binding Path="BranchColor">
                        <Binding.Converter>
                            <local:BrushesConverter></local:BrushesConverter>
                        </Binding.Converter>
                    </Binding>
                </Label.Background>
            </Label>

Hope it's HELPS,
Thanks,
Alex.

יום ראשון, 3 באפריל 2011

Add Double Click Event on TreeNode in Style


Followin code adding MouseDoubleClick Event to TreeView control in Style tag of XAML.
I think it' clear enough without explanations: 

 <TreeView Grid.Row="1"    
  x:Name="MainTreeView"
  Background="Transparent"
  HorizontalAlignment="Stretch"
  ItemsSource="{Binding List}"
  VerticalAlignment="Stretch" 
  ItemTemplate="{StaticResource ClassificationTemplate}" 
                 <TreeView.ItemContainerStyle>
                    <Style TargetType="{x:Type TreeViewItem}">
                        <EventSetter Event="MouseDoubleClick" Handler="OnTreeNodeDoubleClick"/>
                    </Style>
                 </TreeView.ItemContainerStyle>
 </TreeView>


Code Behind:

private void OnTreeNodeDoubleClick(object sender, MouseButtonEventArgs mouseEvtArgs)
{
   .....add your code here                    
}

Find String in WPF TreeView control


How to find string in TreeView control?
The biggest thing about WPF is data binding and so in WPF TreeView very important to what source you binded your TreeView.
For example my source will 3 hierarchical classes A, B and C:

public class A
{
   public string Filed {get; set;}
   List<B> bList = new List<B>();
}

public class B
{
   public string Filed {get; set;}
   List<C> bList = new List<C>();
}

public class C
{
   public string Filed {get; set;}
}


public void Main()
{
   A a = new A();
   B b = new B();
B bb = new B();
C c = new C();
C cc = new C();
C ccc = new C();
a.bList.Add(b);
a.bList.Add(bb);
b.cList.Add(c);
b.cList.Add(cc);
b.cList.Add(ccc);
}


And in XAML we created HierarchicalDataView with relevant DataBinding and got TreeView:

<HierarchicalDataTemplate x:Key="elementTemplate">   
 <StackPanel Orientation="Horizontal">
  <TextBlock Text="{Binding cList}"/>
    <TextBlock Text="{Binding NormaTimeCount, StringFormat= - כמות: {0}}"/>
 </StackPanel>
 </HierarchicalDataTemplate>
 <HierarchicalDataTemplate x:Key="chapterTemplate" 
   ItemsSource="{Binding Path=bList}"                   
   ItemTemplate="{StaticResource elementTemplate}">
   <Grid>
    <Grid.RowDefinitions>
    <RowDefinition></RowDefinition>
    <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
    <ColumnDefinition></ColumnDefinition>
    <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding ld_name}" Grid.Column="0" Grid.Row="0" />
   </Grid>
 </HierarchicalDataTemplate>
 <HierarchicalDataTemplate x:Key="ClassificationTemplate"                            ItemsSource="{Binding Path=Chapters.List}"                   
   ItemTemplate="{StaticResource chapterTemplate}">
   <Grid>
    <Grid.RowDefinitions>
    <RowDefinition></RowDefinition>
    <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
    <ColumnDefinition></ColumnDefinition>
    <ColumnDefinition></ColumnDefinition>
    <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding ld_name}" Grid.Column="0" Grid.Row="0" />
  </Grid>
  </HierarchicalDataTemplate>

C# code:

private void btnFind_Click(object sender, RoutedEventArgs e)
{
   Find(_MainTreeView, textToFindt);
}

private void Find(TreeView mainTreeView, string findToText)
{
   foreach (object item in mainTreeView.Items)
   {
     TreeViewItem treeItem = mainTreeView.ItemContainerGenerator.ContainerFromItem(item) 
                                                                          as TreeViewItem;
                if (treeItem != null)
                    FindAll(treeItem, findText);
                if (item != null)
                {
                    dynamic obje = treeItem.Header;
                    if (isContains(obje.ld_name, findText))
                    {
                        treeItem.Focus();
                        treeItem.IsExpanded = true;
                        treeItem.Background = Brushes.Red;
                    }
                }
            }
        }
 
void FindAll(ItemsControl items, string textToFind)
        {
            foreach (object obj in items.Items)
            {
                ItemsControl childControl = items.ItemContainerGenerator.ContainerFromItem(obj) as ItemsControl;
                if (childControl != null)
                {
                    FindAll(childControl, findText); // Recursion
                }
                TreeViewItem item = childControl as TreeViewItem;
                if (item != null)
                {
                     //only if you dont know type of your Tree (in our case it's A, B or C and you need to use if or switch                        - case statement)
                    dynamic obje = item.Header;                     if (isContains(obje.ld_name, findText))                     {                         item.Focus();                         item.IsExpanded = true; // if you need to expand and show it                         item.Background = Brushes.Red;                     }                 }             }         }

יום שני, 21 במרץ 2011

Build WPF TreeView Control with Data Binding


Today, I would like to show how to build TreeView in WPF with Data Binding, something like this:


Each level of TreeView can be TextBox, TextBlock and even Grid, it's not really matter.
Data Source its data structure (class), that has Generics Lists for all levels of TreeView, of course we fill it before:
public class WorkCatalog
    {
        VehicleModelList _Models = new VehicleModelList();
        WorkClassificationList _Classifications = new WorkClassificationList();
        WorkChapterList _Chapters = new WorkChapterList();
        WorkElementList _Elements = new WorkElementList();    
 
        public VehicleModelList Models
        {
            get { return _Models; }
            set { _Models = value; }
        }
        public WorkClassificationList Classifications
        {
            get { return _Classifications; }
            set { _Classifications = value; }
        }
        public WorkChapterList Chapters
        {
            get { return _Chapters; }
            set { _Chapters = value; }
        }
        public WorkElementList Elements
        {
            get { return _Elements; }
            set { _Elements = value; }
        }
Usual start of WPF partial file: I only added Toolkit of Microsoft in order to use DataGrid:
<UserControl x:Class="GarageManager.Time_Catalog.TimeCatalogUI"
             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" 
             xmlns:ToolKit="http://schemas.microsoft.com/wpf/2008/toolkit"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    //Resources for Styles
    <UserControl.Resources>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="Foreground" Value="Blue"/>
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="FontWeight" Value="Bold" />
        </Style>
   //Resources for Data Template, level that don't have hierarchical levels
        <DataTemplate x:Key="elementTemplate">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding ld_name}" Grid.Column="0" Grid.Row="0" />
 
            </Grid>
        </DataTemplate>
 //Resources for Data Template, level that have hierarchical levels
//If you need more levels - just add one more HierarchicalDataTemplate
        <HierarchicalDataTemplate x:Key="chapterTemplate"
            ItemsSource="{Binding Path=Elements.List}" //Data Binding to Source                  
                ItemTemplate="{StaticResource elementTemplate}">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding ld_name}" Grid.Column="0" Grid.Row="0" />
 
            </Grid>
        </HierarchicalDataTemplate>
        
        <HierarchicalDataTemplate x:Key="ClassificationTemplate"
                ItemsSource="{Binding Path=Chapters.List}" //Data Binding to Source          
                ItemTemplate="{StaticResource chapterTemplate}">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>
//Here you can put all data you can see on this level of TreeView: Text, Grid anything else:
                <TextBlock Text="{Binding ld_name}" Grid.Column="0" Grid.Row="0" />
                <TextBlock Text="{Binding ld_code}" Grid.Column="1" Grid.Row="0"/>             
            </Grid>
        </HierarchicalDataTemplate>
    </UserControl.Resources>
    
    <StackPanel Name="Global" FlowDirection="RightToLeft">
        <TreeView Margin="10,10,0,13" Name="TreeView1"
                  HorizontalAlignment="Left"
            ItemsSource="{Binding List}"
            VerticalAlignment="Top" Height="400"          
            ItemTemplate="{StaticResource ClassificationTemplate}">            
        </TreeView>                
    </StackPanel>    
</UserControl>

ListBox Control - Create Custom Layouts (Vertical and Horizontal)


There are no anymore multicolumn property in WPF ListBox, like in WinForm.
There are couple of more flexible options. Following please find how to add custom layouts to ListBox:

You have couple of options:

1.       DockPanel
2.       StackPanle
3.       WrapPanel

And supposed you have:

ListBox myListBox = new ListBox();
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(WrapPanel));
factory.SetValue(WrapPanel.HorizontalAlignmentProperty, HorizontalAlignment.Left); //not necessary
myListBox.ItemsPanel = new ItemsPanelTemplate(factory);

and change it back to default ListBox layout that you see regulary:

FrameworkElementFactory factory = new FrameworkElementFactory(typeof(StackPanel));
factory.SetValue(StackPanel.OrientationProperty, Orientation.Vertical);
myListBox.ItemsPanel = new ItemsPanelTemplate(factory);

May be more difficult, but more flexible, isn't it? J

יום חמישי, 17 במרץ 2011

WPF - DRAG&DROP objects in ListBox

I would like to show example of how to drug and drop objects between different ListBox.
All starting with Mouse PreviewMouseLeftButtonDown Event and finished (more important) with GarageEntryListBox_Drop (see below).


private void GarageEntryListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {           
            ListBox parent = (ListBox)sender;
            Point point  = e.GetPosition(parent) ;
            UIElement element = parent.InputHitTest(point) as UIElement;            
            UIElement panel = GetParent<GarageEntryPanel>(element, parent);
            object data = GetDataFromListBox(parent, panel);           
            if (data != null)
            {
                DragDrop.DoDragDrop(parent, data, DragDropEffects.Move);
            }
        }
 
        private object GetDataFromListBox(ListBox source, UIElement element)
        {
            UIElement result = GetParent<GarageEntryPanel>(element, source);
            return result;
        }
 
        public UIElement GetParent<TSource>(UIElement element, UIElement stopOn)
        {
 
            while (element != null && !(element is TSource) && (element != stopOn))
            {
                element = VisualTreeHelper.GetParent(element) as UIElement;               
            }
            if (element == stopOn)
                return null;
            else
                return element;
            
        }
 
        private void GarageEntryListBox_Drop(object sender, DragEventArgs e)
        {
            ListBox currentListBox = (ListBox)sender;
            GarageEntryPanel data = e.Data.GetData(typeof(GarageEntryPanel)) 
                                                           as GarageEntryPanel;
            if(data == null) 
                return;
            ListBox sourceList = GetParent<ListBox>(data, null) as ListBox;
            if (sourceList == currentListBox)
                return;
            ld_garageentry entry = data.DataContext as ld_garageentry;           
            ((IList)sourceList.ItemsSource).Remove(entry);
            ((IList)currentListBox.ItemsSource).Add(entry);                            
        }