Feeds:
Posts
Comments

Archive for the ‘WPF’ Category

I discovered this article, WPF Localization Using RESX Files, on codeproject.com when I was thinking about how to localize my WPF application, BarModelMaker. After having examined the sample code of the article, I decided to give it a try, and I successfully localized BarModelMaker to Simplified Chinese in 2 hours.

Thanks Grant Frisken for the excellent solution!

 

Advertisements

Read Full Post »

Bar Model Maker

I am developing a Windows desktop application, called Bar Model Maker, that allows you to draw Bar Models by writing Bar Model Script. The tool displays the bar model as you are writing the script. Once you’ve specified enough constraints, you will get the accurate bar model.

The user interface will be something as the following screen shot. Beginners can create a bar model by using the dialog boxes on the right. Advanced users can write script directly in the script editor.

Bar Model Maker

 

Read Full Post »

Customize WPF ComboBox

There is a column in the Lines ListView of the Edit Model dialog box of the GeometryViz3D tool for the user to choose the color of the lines. Previously the colors are shown as plain text in the ComboBoxes. It will be more user friendly if the colors can be illustrated as the actual colors as shown in the following screen shot.

Here is the XAML code I use to achieve this. The key idea is customize the ItemTemplate of the ComboBoxes bound to the Color column of the Lines ListView by drawing a Rectangle.

The ItemSource property of the ComboBox is bound to the Colors property of the EditModelViewModel object, which is an array of strings that are color names, including Black, Cyan, and so on. And therefore, each item of the ComboBox is bound to the name of a color. We bind the color name to the Fill property of the Rectangle by taking advantage of the WPF’s built-in feature that converts a color name to a Brush.

The remaining issue is how to specify the size of the Rectangle. If we don’t specify, the actual height and width will both be 0. After some research using the snoop tool, I decided to determine the Height based on the FontSize of the ComboBox, and the Width based on the ActualWidth the ComboBox. Since the ComboBox is an ancestor of the items of the drop down list, we use the ComboBox as the RelativeSource. To make the Height of the ComboBox similar to the ComboBoxes in other columns, we have to adjust the value a little bit. This is where the DoubleAdditionConverter comes in. We make the Height to be FontSize + 2, and Width to be ActualWidth – 4.

Since the Height and Width of the Rectangle will not change after initialized, we set the binding Mode to OneTime.

The SelectedValue property of the ComboBox is bound to the Color property of the G3DLine object that is bound to the ListViewItem.

<Window.Resources>
  <local:DoubleAdditionConverter x:Key="doubleAdditionConverter" />
  …
  <DataTemplate x:Key="colorCellTemplate">
    <ComboBox ItemsSource="{Binding Colors}"
              SelectedValue="{Binding Color}"
              Margin="-6, 0, -6, 0" >
      <ComboBox.ItemTemplate>
        <DataTemplate>
          <Rectangle Fill="{Binding Mode=OneTime}"
                     Height="{Binding Mode=OneTime,
                       RelativeSource={RelativeSource FindAncestor,
                         AncestorType={x:Type ComboBox}},
                       Path=FontSize,
                       Converter={StaticResource doubleAdditionConverter},
                       ConverterParameter=2}"

                     Width="{Binding Mode=OneTime,
                       RelativeSource={RelativeSource FindAncestor,
                         AncestorType={x:Type ComboBox}},
                       Path=ActualWidth,
                       Converter={StaticResource doubleAdditionConverter},
                       ConverterParameter=-4}" />
        </DataTemplate>
      </ComboBox.ItemTemplate>
    </ComboBox>
  </DataTemplate>
</Window.Resources>

<ListView Margin="3"
          ItemsSource="{Binding Lines}"
          SelectedValue="{Binding SelectedLine}">
  <ListView.View>
    <GridView>
      <GridView.Columns>
        …
        <GridViewColumn Header="Color" Width="120"
                        CellTemplate="{StaticResource colorCellTemplate}" />
      </GridView.Columns>
    </GridView>
  </ListView.View>
</ListView>

Here is the source code of the DoubleAdditionConverter:

public class DoubleAdditionConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        double v = (double)value;
        double p = Double.Parse((string)parameter);
        return v + p;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        double v = (double)value;
        double p = Double.Parse((string)parameter);
        return v - p;
    }

    #endregion
}

The entire source code can be found at the GeometryViz3D project on CodePlex.

Read Full Post »

Published an article on CodeProject, discussing about the issues related to binding ObservableCollection to ListView and ComboBox controls. Please read the article here.

Read Full Post »

In WPF, we can bind an ObservableCollection<T> object to the ItemsSource property of an ItemsControl, e.g. a ComboBox. When we add or remove an element from the collection, the control bound to the collection will get updated automatically. However, we modify an element of the collection, the control won’t get updated. To force the control gets updated when an element of the collection it is bound to, we need to write a class inheriting from ObservableCollection<T> and provide a method that calls the OnCollectionChanged() method, which raises the CollectionChanged event, which will caused the control bound to the collection to be refreshed.

public class ElementCollection<T> : ObservableCollection<T>
{
  public void UpdateCollection()
  {
    OnCollectionChanged(new NotifyCollectionChangedEventArgs(
        NotifyCollectionChangedAction.Reset));
  }
}

Then we change the type of the collection to ElementCollection, and call the UpdateCollection() method after we changed the value of an element.

Read Full Post »

CommandManager.InvalidateRequerySuggested()

In a WPF application, I have several Button controls each bound to a ICommand property of a ViewModel. In one of the method of a ViewModel, I invoke a method with a worker thread. When the method is done, it will change the value of a property, which would causes the CanExecute() method of the ICommand objects to return a different value, which would, in turn, cause the Button controls to be enabled or disabled accordingly. But nothing changes. After doing some research, I’ve found that we can call the CommandManager.InvalidateRequerySuggested() method to force the Buttons bound to ICommand object to be refreshed.

private void DoSomething()
{
    ThreadPool.QueueUserWorkItem(state =>
        {
            DoSomethingInBackgroud();
            SynchronizationContext.Current.Post(s =>
                {
                    CommandManager.InvalidateRequerySuggested();
                }, null);
       });
}

The DoSomething() method will be called when the Execute() method of the ICommand object bound to a button is called. The ThreadPool.QueueUserWorkItem() method queues the lambda code block passed in to wait its turn to be run by a worker thread. Once the DoSomethingInBackground() method is done by the worker thread, another lambda code block containing the call to the CommandManager.InvalidateRequerySuggested() method is queued to the UI thread, represented by SynchronizationContext.Current, to make sure that this method will be executed by the UI thread.

Read Full Post »