views:

591

answers:

4

My scenario: A wpf form has a textbox and a wpf toolkig datagrid. When a text is entered in the textbox, my service returns an IEnumerable<TranslationItem> items. I want my datagrid to show the result of this service.

I tried googling around, but I can't get heads or tails to it. I'm only starting to learn WPF and most of the used terms elude me.
What I gather is that I should put my service's result in an ObservableCollection, no sweat. But then I want to somehow bind it to my datagrid. How can I do that? How will the grid know what columns to generate?

A: 

You set the DataSource (or even the DataContext) of the grid to your Observable Collection.

I'm not familiar with that data grid, but most grids have options to either expose all public properties of the type in the Observable Collection as columns, or you explicitly set a column layout in XAML and one of the properties of the column definition is the property of the the object to use for the column data.

e.g. with Infragistics Data Grid

                <igDP:Field Name="OrderSize" Label="Order Size">
                    <igDP:Field.Settings >
                        <igDP:FieldSettings CellWidth="75">
                            <igDP:FieldSettings.EditorStyle>
                                <Style TargetType="{x:Type Editors:ValueEditor}" >
                                    <Style.Setters>
                                        <Setter Property="Format" Value="#,##0"/>
                                    </Style.Setters>
                                </Style>
                            </igDP:FieldSettings.EditorStyle>
                        </igDP:FieldSettings>
                    </igDP:Field.Settings>
                </igDP:Field>

Name is where you set the property on the object to use.

Gus Paul
A: 

your grid can either build the columns directly, or you can specify the column types that you wish. If you watch this video, it'll explain it. That's for VS2010, but the fundamentals are the same for VS2008--though implementation is just slightly different, as it's not quite as integrated.

As for how to bind, assign the ObservableCollection that holds your items to the ItemsSource property of the grid.

Stephen Wrighton
+3  A: 

What I gather is that I should put my service's result in an ObservableCollection, no sweat. But then I want to somehow bind it to my datagrid. How can I do that?

The easiest way is to set the ItemsSource property of the DataGrid to the ObservableCollection.

How will the grid know what columns to generate?

The DataGrid reflects the objects in that collection and create a column for each public property it finds. See here for more info.

If you set the ItemsSource property directly then it's not really wpf binding. Here's three links I found useful when I started databinding in WPF.

Bea Stollnitz: What does “{Binding}” mean?
WPF Basic Data Binding FAQ
MSDN: Data Binding How-to Topics

Cameron MacFarland
Looks like there's some good reading in here. Don't have time to check them now, but will return after I have.
borisCallens
+2  A: 

While an ObservableCollection can be used for this, depending on how it is used you won't recive any benifit from it. The key feature of the ObservableCollection is that it implements INotifyCollectionChanged. What this interface does is provide a notifcation mechanism to tell the UI that a property has changed. Since ObservableCollection already implements this, if you bind your DataGrid, ListBox, ItemsControl, etc.'s ItemSource property to a collection of this type it will automaticly update the UI any time an item is Added/Removed/Replaced/Moved/Reset. Because of this, every time you want to update the collection with a new IEnumerable result set, you will have to first clear the collection, and then add the new results.

However, there is another option that I would recommend over an ObservableCollection in this case. It is to use something called an ObjectDataProvider. Using this we can avoid the code behind entirely, and it is overall much cleaner. So we have our service somewhere, in this case in my Window.xaml.cs

public class TranslationService
{
 public IEnumerable<string> Translate(string s)
 {
  return s.ToCharArray().Select(c => c.ToString());
 }
}

Like the service you describe, it takes it a string from a textbox, and returns an IEnumerable. Now, in the XAML we can use this service and make calls to it.

In the window declerations, we add the namespace for where the service is located:

 xmlns:local="clr-namespace:WpfApplication4"

Now, in our Window.Resources (Or UserControl, or anywhere else) we can reference our service. Once we have exposed our service as a resource, we can create an ObjectDataProvider that exposes the Translate method we wish to use.

<Window.Resources>
 <local:TranslationService x:Key="MyTranslationService" />
 <ObjectDataProvider x:Key="MyProvider"
      ObjectInstance="{StaticResource MyTranslationService}"
      MethodName="Translate">
  <ObjectDataProvider.MethodParameters>
   ""
  </ObjectDataProvider.MethodParameters>
 </ObjectDataProvider>
</Window.Resources>

The ObjectDataProvider is keyed to our Service and calls the Translate method with a String parameter. Now all we have to do is get it to respond to our text box.

We can do this by making use of some of the Binding properties. We want our TextProperty in the TextBox to bind to the ObjectDataProvider, so we set the Source property to point to it. The part of the ObjectDataProvider that we want to bind to, in the Path, is the MethodParameter. Now, we set it to Bind directly to the source of that property, and to only travel one way, meaning that the ObjectDataProvider's method parameter won't update the TextBox's text. Finally we can set the UpdateSourceTrigger to PropertyChanged, telling the binding to set the source we are binding to, in the object data provider, whenever there is any change to the text.

<StackPanel>
  <TextBox TextChanged="OnTextChanged"
   Text="{Binding Source={StaticResource MyProvider}, Path=MethodParameters[0], BindsDirectlyToSource=True, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
  <ListBox ItemsSource="{Binding Source={StaticResource MyProvider}}" />
 </StackPanel>

All that's left is to set the ItemsSource in the Grid, or a simple ListBox in this case.

Regarding the final part on the DataGrid: If you are using the WPFToolkit's data grid, it does have an auto generate feature that can be set through the properties, and you can find more info on it here.

rmoore
Thanks, I think I can apply this example with little changes. I'm not going to call my service directly as I wouldn't want litterly every keystroke to trigger a call to my service (and thus my translation db), but rather have a little timer buffer in between. But other than that it looks promissing.
borisCallens