Autor Beitrag
UGrohne
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Fr 27.02.09 12:42 
Hallo,

ich schreibe gerade eine Applikation im MVVM-Pattern, in der mehrere Listen angezeigt werden sollen. Dazu möchte ich die Verwaltung der Listenansichten gerne in einer Basisklasse zentralisieren, vor allem um später die Wiederverwendbarkeit beispielsweise für Suchdialog zu gewährleisten. Problem ist nun, dass jede spezialisierte Liste über eigene Spalten verfügt, die angezeigt werden sollen, daher muss ich diese Spalten dynamisch aufbauen. Ich möchte aber nach dem Prinzip des MVVM keine direkte Verknüpfung zwischen XAML-View und der ModelView-Klasse haben, daher das gerne über DataBinding lösen. Außerdem kann ich darüber dann sehr einfach Sortier-, Gruppier- und Filterfunktionen bereitstellen.

Und nun das Problem: Ich dachte mir, ich könnte mir einfach eine List<GridViewColumn> bauen, die mir die anzuzeigenden Spalten enthält und binde diese per {Binding} an den Content des GridView-Elements innerhalb der Listview.
Doch dummerweise kann ich weder im Content, noch in der ColumnCollectionProperty des GridView ein Binding einbauen. Wie kann ich das Problem jetzt lösen?

Grüße,

Uwe
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Fr 27.02.09 15:24 
Interessantes Problem, mal wieder :D .

Es gibt zwar eine Attached Property ColumnCollection aber...
Zitat:
Do not use this method to change the columns collection for a GridView object. To get, set, or change the columns that are defined for a GridView object, use the Columns property.

:mrgreen: .

Also musst du doch über Columns.Add gehen, am einfachsten wahrscheinlich im Code-Behind. Für Dynamisches wäre das DataGrid aus dem WPF-Toolkit wohl eher geeignet ;) .

Aber ich frage mich gerade: Gehören GridViewColumns denn in das View Model? Ich würde dann doch für jede Datenquelle ein passendes GridView in den Ressourcen festlegen. Fragt sich nur noch, wie du das passende dann der (dem :gruebel: ? Argh) ListView zuordnest. Dafür könntest du im VM eine Property Type ItemType festlegen und diese in einem Style für die ListView über Data Trigger auswerten, also etwa:
ausblenden XML-Daten
1:
2:
3:
<Style.Triggers>
  <DataTrigger Binding="{Binding ItemType}" Value="{x:Type ItemA}">
    <Setter Property="View" Value="{StaticResource GridViewForItemA}" />

_________________
>λ=
UGrohne Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Fr 27.02.09 16:00 
War ja klar, dass Du darauf antwortest :P

Die CodeBehinds will ich überhaupt nicht nutzen ;).

Ja, das ins VM wieder unterzubringen ist evtl. nicht ganz sauber, aber ich will die Möglichkeit haben, allgemein das zu bestimmen, was angezeigt werden soll, aber dem Benutzer auch die Möglichkeiten bieten (später) weitere Spalten hinzufügen zu können.

Und gerade für ein Suchwindow, das dann alle ListViewModels (dafür habe ich so eine Basisklasse erstellt, die schon eine generische ObservableCollection bereitstellt, die von den Nachfahren nur noch gefüllt werden muss) unterstützt, wäre das natürlich genial.

Ist das DataGrid denn inzwishcen final? Ich hab mir das noch nie angeschaut.

Vielleicht komme ich heute Abend nochmal dazu, mir das anzuschauen.
UGrohne Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Fr 27.02.09 17:23 
Hab mir eben schnell das DataGrid angeschaut, das bringt mir in der Hinsicht noch weniger, weil die Columns-Collection gar nicht das DependencyProperty, sondern als ObservableCollection implementiert ist.

Oder hattest Du da an ein anderes Vorgehen gedacht? Ich will jedenfalls auf keinen Fall direkt auf das Grid zugreifen müssen. Unter Umständen muss ich ansonsten eben ein eigenes Control vom GridView ableiten und diese Collection als DependencyProperty implementieren.
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Fr 27.02.09 18:00 
user profile iconUGrohne hat folgendes geschrieben Zum zitierten Posting springen:
Ist das DataGrid denn inzwishcen final? Ich hab mir das noch nie angeschaut.
In Entwicklung ist es noch, aber die erste Version heist zumindest nicht mehr "Beta".
user profile iconUGrohne hat folgendes geschrieben Zum zitierten Posting springen:
Oder hattest Du da an ein anderes Vorgehen gedacht?
Ich dachte da eher so an AutoGenerateColumns :mrgreen: .
user profile iconUGrohne hat folgendes geschrieben Zum zitierten Posting springen:
Ich will jedenfalls auf keinen Fall direkt auf das Grid zugreifen müssen. Unter Umständen muss ich ansonsten eben ein eigenes Control vom GridView ableiten und diese Collection als DependencyProperty implementieren.
Ableiten ist bei WPF meistens unnötig ;) : www.codeproject.com/...tachedBehaviors.aspx

In etwas so:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
  public static class GridViewHelper
  {
    public static readonly DependencyProperty ColumnBindingPathsProperty =
      DependencyProperty.RegisterAttached("ColumnBindingPaths"typeof(IEnumerable<string>), typeof(GridViewHelper), new UIPropertyMetadata(
        (d, e) => {
          GridView view = (GridView)d;
          view.Columns.Clear();
          foreach (string path in (IEnumerable<string>)e.NewValue)
            view.Columns.Add(new GridViewColumn { DisplayMemberBinding = new Binding(path), Header = path });
        }
      ));

    public static IEnumerable<string> GetColumnBindingPaths(DependencyObject obj)
    {
      return (IEnumerable<string>)obj.GetValue(ColumnBindingPathsProperty);
    }

    public static void SetColumnBindingPaths(DependencyObject obj, IEnumerable<string> value)
    {
      obj.SetValue(ColumnBindingPathsProperty, value);
    }
  }

ausblenden XML-Daten
1:
2:
3:
      <ListView.View>
            <GridView my:GridViewHelper.ColumnBindingPaths="{Binding ColumnPaths}" />
        </ListView.View>

_________________
>λ=
UGrohne Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Veteran
Beiträge: 5502
Erhaltene Danke: 220

Windows 8 , Server 2012
D7 Pro, VS.NET 2012 (C#)
BeitragVerfasst: Fr 27.02.09 22:29 
user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconUGrohne hat folgendes geschrieben Zum zitierten Posting springen:
Oder hattest Du da an ein anderes Vorgehen gedacht?
Ich dachte da eher so an AutoGenerateColumns :mrgreen: .

Das bringt mir aber nichts, weil ich ja nicht alle Inhalte zeigen will und ich außerdem mit Objekten arbeite. Wenn da alles angezeigt wird, dann brauch ich zwei 24"-Monitore nebeneinander ;).

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconUGrohne hat folgendes geschrieben Zum zitierten Posting springen:
Ich will jedenfalls auf keinen Fall direkt auf das Grid zugreifen müssen. Unter Umständen muss ich ansonsten eben ein eigenes Control vom GridView ableiten und diese Collection als DependencyProperty implementieren.
Ableiten ist bei WPF meistens unnötig ;) : www.codeproject.com/...tachedBehaviors.aspx

In etwas so:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
  public static class GridViewHelper
  {
    public static readonly DependencyProperty ColumnBindingPathsProperty =
      DependencyProperty.RegisterAttached("ColumnBindingPaths"typeof(IEnumerable<string>), typeof(GridViewHelper), new UIPropertyMetadata(
        (d, e) => {
          GridView view = (GridView)d;
          view.Columns.Clear();
          foreach (string path in (IEnumerable<string>)e.NewValue)
            view.Columns.Add(new GridViewColumn { DisplayMemberBinding = new Binding(path), Header = path });
        }
      ));

    public static IEnumerable<string> GetColumnBindingPaths(DependencyObject obj)
    {
      return (IEnumerable<string>)obj.GetValue(ColumnBindingPathsProperty);
    }

    public static void SetColumnBindingPaths(DependencyObject obj, IEnumerable<string> value)
    {
      obj.SetValue(ColumnBindingPathsProperty, value);
    }
  }

ausblenden XML-Daten
1:
2:
3:
      <ListView.View>
            <GridView my:GridViewHelper.ColumnBindingPaths="{Binding ColumnPaths}" />
        </ListView.View>

Das werd ich mal ausprobieren, danke :)

//EDIT: Funktioniert einwandfrei. Werde ich noch etwas verbessern, um weitere Funktionen, aber sieht schon sehr gut aus :)