Showing posts with label ComboBox. Show all posts
Showing posts with label ComboBox. Show all posts

Monday, February 7, 2011

Faster Controls with UI Virtualization in WPF

A very useful feature in WPF for making faster and interactive UIs is UI virtualization. Let's say, in your WPF application, you have a ComboBox with 10,000 elements in it. When you will click the ComboBox to expand it, it will take quite some time before actually the ComboBox's available options will be displayed in a drop down for selection. The reason is simple, loading 10,000 UI elements in WPF's visual tree naturally takes some time. One thing to keep in mind is that whether your combo box contains 10K element or 100K elements, user will be looking at a very limited number of options at one instance of time. For example if you expand a combo box, at one time you will only be looking at approximately 25 elements. Even if at one time we see only limited amount of items, WPF loads all the item in visual tree when you click expand button and it takes memory and time. By enabling UI virtualization in your ComboBox, WPF will create only those elements in memory which you can see at one time. This will make combo box expand in no time and as a result even if your combo box contains 10K elements, WPF will take no time to expand it. As you will move the scroll bar down to see more items in ComboBox, WPF will dynamically dispose the elements which will go out of the view as a result of scrolling and will create & load new items in ComboBox accordingly. 
Some controls uses UI Virtualization by default like ListBox and ListView but some controls don't for example ComboBox. You can use following XAML to enable UI virtualization in a combo box and similarly in any control you want.

   1:  <ComboBox ItemsSource="{Binding Path=Collection}">
   2:       <!--comment ComboBox.ItemsPanel below to see how much time WPF takes to load this ComboBox without UI virtualization-->
   3:       <ComboBox.ItemsPanel>
   4:            <ItemsPanelTemplate>
   5:                 <VirtualizingStackPanel />
   6:            </ItemsPanelTemplate>
   7:        </ComboBox.ItemsPanel>
   8:  </ComboBox>
I have uploaded an application which you can use to see the difference in loading time with and without UI virtualization. You can download this application from here. Comment the ComboBox.ItemPanel in XAML to note the time it takes without UI Virtualization and uncomment ComboBoc.ItemPanel to note time it takes with UI Virtualization.

Sunday, January 23, 2011

Create DataGrid in WPF using code

In this post I will explain a way to create DataGrid in WPF through code. I will use a DataTable with two columns as a DataSource for the DataGrid. I will display one column as TextBox while the second column will be displayed as ComboBox. You can download complete sample from here. My main Window contains single Grid inside which I will display my DataGrid. I will use MainGrid_Initialized event to create DataGrid.


 Inside MainGrid_Initialized event I am creating a DataGrid and adding two columns inside it. One for the TextBox and other for the ComboBox. Then I am setting a DataTable as DataSource for my DataGrid which contains two columns "TextBoxColumn" and "ComboBoxColumn". At the end, I am placing my DataGrid in the Main Window's Grid.
   1:  DataGrid dataGrid = new DataGrid();
   2:  dataGrid.AutoGenerateColumns = false;
   3:   
   4:  dataGrid.Columns.Add(CreateTextBoxColumn());
   5:  dataGrid.Columns.Add(CreateComboBoxColumn());
   6:  //set DataTable as item source of dataGrid
   7:  dataGrid.ItemsSource = GetDataTable().AsDataView();
   8:   
   9:  //place DataGrid inside main Grid
  10:  Grid.SetColumn(dataGrid,0);
  11:  Grid.SetRow(dataGrid,0);
  12:  MainGrid.Children.Add(dataGrid);
In CreateTextBoxColumn method I am defining my TextBox based column. I am creating two templates one for viewing(TextBlock) and other for editing (TextBox). I am also creating bindings between DataTable's "TextBoxColumn" with the TextBlock and the TextBox templates.

   1:  private DataGridTemplateColumn CreateTextBoxColumn()
   2:          {
   3:              //create a template column
   4:              DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();
   5:              //set title of column
   6:              templateColumn.Header = "TextBoxColumn";
   7:              //non editing cell template.. will be used for viweing data
   8:              DataTemplate textBlockTemplate = new DataTemplate();
   9:              FrameworkElementFactory textBlockElement = new FrameworkElementFactory(typeof(TextBlock));
  10:              Binding textBlockBinding = new Binding("TextBoxColumn");
  11:              textBlockElement.SetBinding(TextBlock.TextProperty, textBlockBinding);
  12:              textBlockTemplate.VisualTree = textBlockElement;
  13:              templateColumn.CellTemplate = textBlockTemplate;
  14:   
  15:              //editing cell template ... will be used when user will edit the data
  16:              DataTemplate textBoxTemplate = new DataTemplate();
  17:              FrameworkElementFactory textboxElement = new FrameworkElementFactory(typeof(TextBox));
  18:              Binding textboxBinding = new Binding("TextBoxColumn");
  19:              textboxElement.SetBinding(TextBox.TextProperty, textboxBinding);
  20:              textBoxTemplate.VisualTree = textboxElement;
  21:              templateColumn.CellEditingTemplate = textBoxTemplate;
  22:              return templateColumn;
  23:          }
Similarly, I am creating a column for ComboBox

   1:   private DataGridTemplateColumn CreateComboBoxColumn()
   2:          {
   3:              //create a template column
   4:              DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();
   5:              //set title of column
   6:              templateColumn.Header = "ComboBoxColumn";
   7:              //non editing cell template.. will be used for viweing data
   8:              DataTemplate textBlockTemplate = new DataTemplate();
   9:              FrameworkElementFactory textBlockElement = new FrameworkElementFactory(typeof(TextBlock));
  10:              Binding textBlockBinding = new Binding("ComboBoxColumn");
  11:              textBlockElement.SetBinding(TextBlock.TextProperty, textBlockBinding);
  12:              textBlockTemplate.VisualTree = textBlockElement;
  13:              templateColumn.CellTemplate = textBlockTemplate;
  14:   
  15:              //editing cell template ... will be used when user will edit the data
  16:              DataTemplate comboboxTemplate = new DataTemplate();
  17:              FrameworkElementFactory comboboxElement = new FrameworkElementFactory(typeof(ComboBox));
  18:              Binding comboboxBinding = new Binding("ComboBoxColumn");
  19:              comboboxElement.SetBinding(ComboBox.TextProperty, comboboxBinding);
  20:   
  21:              //combo box will show these options to select from
  22:              comboboxElement.SetValue(ComboBox.ItemsSourceProperty, new List<string> { "Value1", "Value2" ,"Value3", "Value4" });
  23:              comboboxTemplate.VisualTree = comboboxElement;
  24:              templateColumn.CellEditingTemplate = comboboxTemplate;
  25:              return templateColumn;
  26:          }
You can download complete sample from here and play with it

Saturday, January 22, 2011

ComboBox in WPF’s DataGrid through XAML

One thing I did recently was to allow user select value of a cell in WPF’s DataGrid from a ComboBox. I was using MVVM and the DataGrid was in my view so I had to define the columns and their bindings through XAML. After reading some articles on internet and some experimentation I was able to achieve the goal and I am sharing my solution here.

   1:  <DataGrid ItemsSource=”{Binding Path=CustomObjectCollection}” 
   2:   AutoGenerateColumns=”False>
   3:   <DataGrid.Columns>
   4:    <DataGridTemplateColumn>
   5:     <DataGridTemplateColumn.CellTemplate>
   6:      <DataTemplate>
   7:       <TextBlock Text=”{Binding Path=CustomObjectStringMember}”/>
   8:      </DataTemplate>
   9:     </DataGridTemplateColumn.CellTemplate>
  10:     <DataGridTemplateColumn.CellEditingTemplate >
  11:      <DataTemplate>
  12:       <ComboBox ItemsSource=”{Binding Path=CustomObjectListMember}” 
  13:       Text=”{Binding Path=CustomObjectStringMember}”/>
  14:      </DataTemplate>
  15:     </DataGridTemplateColumn.CellEditingTemplate>
  16:    </DataGridTemplateColumn>
  17:   </DataGrid.Columns>
  18:  </DataGrid>
In the code snippet above, CustomObjectCollection is an ObservableCollection where CustomObject is custom class I have written. Each instance of CustomObject in my ObservableCollection represents one row in my DataGrid. My CustomObject has two members ‘CustomObjectStringMember’ which is a String and ‘CustomObjectListMember’ which is List. The ComboBox in DataGrid will display the items in ‘CustomObjectListMember’ as possible options for selection and the selected value will be stored in ‘CustomObjectStringMember’ variable of my ‘CustomObject’ instance.

I have defined two templates inside XAML of my column. First one i.e. DataGridTemplateColumn.CellTemplate displays the selected value as TextBlock when the user is only viewing the the data in DataGrid. Second i.e. DataGridTemplateColumn.CellEditingTemplate will be activated when user will click on particular cell to edit its value. This template will update the cell from TextBlock to ComboBox and will allow user to select any value from the ComboBox. As soon as the cell will lose focus the CellTemplate will again replace the CellEditingTemplate