views:

56

answers:

3

Regard this image: alt text

The TimeTableViewModel of this user interface is this:

public string SchoolclassCodeMonday {get;set;}
public string SchoolclassCodeTuesday {get;set;}
public string SchoolclassCodeWednesday {get;set;}
public string SchoolclassCodeThursday {get;set;}
public string SchoolclassCodeFriday {get;set;}
public string SchoolclassCodeSaturday {get;set;}
public string SchoolclassCodeSunday {get;set;}

The above would work when I would display only the properties as string in a textbox. But what I want is to bind each combox to a mutual ObservableCollection SchoolclassCodes and the SelectedItem aka DisplayMember of the ComboBox must somehow be MAPPED to one of the 7 above Properties AND the SelectedItem if retrieved must supply a row of all 7 schoolclass selected in the combobox.

Or what I actually want just in other words ;-)

Display in the ComboBox the SchoolclassCodes list, set the Value of SelectedItem.SchoolclassCode"WeekdayName" to the Value of the selected ComboboxItem.SchoolclassCode

Well there are some ideas I have but all lack some experience to make them fully working.

I could add to the TimeTableViewModel for each Property a ObservableCollection SchoolclassCodes but that seems very redundant to me. Why should I hold 7 lists for ONE row when each cell has the same list with the same items in it ?

Any suggestions concerning the ViewModels structure and the binding in Wpf are welcome :)

UPDATE: My SchoolclassCodes list is dynamically created, so I there is no possibility about static binding or hardcode string items in XAML...

UPDATE2:

OK I tried to make it working with MVVM:

I had to change the ObservableCollection ClassCodes to ObservableCollection SchoolclassCodes as the Schoolclass object has a reference to Pupil class with strings thats not possible.

Schoolclass.cs:

public string SchoolclassCode {get;set;}
...

TimeTableWeekViewModel.cs:

public ObservableCollection<Schoolclass> SchoolclassCodes
    {
        get { return _schoolclassCodes; }
        set
        {
            _schoolclassCodes = value;
            this.RaisePropertyChanged("SchoolclassCodes");
        }
    }

XAML:

How must the binding look like NOW because the SchoolclassCodes is not found by wpf ?

A: 

Since the columns must be auto generated, putting this code in the appropriate place (Loaded event?) should do what you want :

        ObservableCollection<String> Collection = GetCollection();

        foreach (DataGridComboBoxColumn column in DataGrid1.Columns.OfType<DataGridComboBoxColumn>())
        {
            column.ItemsSource = Collection;
        }

Of course, some modifications must be applied!

decyclone
As I have not yet made final decision about the TimeTableViewModel structure I can not say wether this is wrong/right what you suggest, all what I know is there must be only ONE list in the VIEWMODEL that all DataGridComboBoxColumns and their ItemsSource bind to! So I have no need/use for your advise :/Please only suggestions with XAML and/or binding to a ViewModel.
msfanboy
+1  A: 

I recommend that you look into the MVVM (Model-View-ViewModel) pattern if you are doing anything remotely complicated. Also, it is useful to implement INotifyPropertyChanged in your model/viewmodel (e.g. the MyWeek class in my example below). That will notify any other bindings whenever one of your SchoolclassCode<Day> properties is changed.

Here is some simple sample code to get you started:

using System.Collections.ObjectModel;
using System.Linq;

namespace BindingSample
{
    public partial class Window1
    {
        public Window1()
        {
            InitializeComponent();
            SchoolclassCodes = new ObservableCollection<string>(
                Enumerable.Range(1, 10).Select(i => "Code #" + i));
            MyWeeks = new ObservableCollection<MyWeek>(
                Enumerable.Range(1, 5).Select(i => new MyWeek() {SchoolclassCodeMonday = SchoolclassCodes.First()}));
            DataContext = this;
        }

        public ObservableCollection<string> SchoolclassCodes { get; private set; }
        public ObservableCollection<MyWeek> MyWeeks { get; private set; }
    }

    public class MyWeek
    {
        public string SchoolclassCodeMonday { get; set; }
        public string SchoolclassCodeTuesday { get; set; }
        public string SchoolclassCodeWednesday { get; set; }
        public string SchoolclassCodeThursday { get; set; }
        public string SchoolclassCodeFriday { get; set; }
        public string SchoolclassCodeSaturday { get; set; }
        public string SchoolclassCodeSunday { get; set; }
    }
}

<Window x:Class="BindingSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <CollectionViewSource x:Key="ClassCodes" Source="{Binding SchoolclassCodes}" />
    </Window.Resources>
    <ListView ItemsSource="{Binding MyWeeks}">
        <ListView.View>
            <GridView>
                <GridView.Columns>
                    <GridViewColumn Header="Monday">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox SelectedValue="{Binding SchoolclassCodeMonday}"
                                          ItemsSource="{Binding Source={StaticResource ClassCodes}}"
                                          IsSynchronizedWithCurrentItem="False" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <!-- Other columns here... -->
                </GridView.Columns>
            </GridView>
        </ListView.View>
    </ListView>
</Window>
Joseph Sturtevant
quote:"I recommend that you look into the MVVM (Model-View-ViewModel) pattern if you are doing anything remotely complicated."I already use it... its a TimeTableViewModel ;-)quote:" Also, it is useful to implement INotifyPropertyChanged in your model/viewmodel (e.g. the MyWeek class in my example below). That will notify any other bindings whenever one of your SchoolclassCode<Day> properties is changed." I already do that in my TimeTableViewModel inheriting from ViewModelBase, I just removed the this.RaisePropertyChanged("..."); for easier reading.thx for the code I will look into it.
msfanboy
ok I updated my init post having now a mvvm binding question to you Joseph :)
msfanboy
A: 

You don't need to mess around with the DataGrid, and you certainly don't need to create any class with seven different properties that you have to implement property-change notification for. The Grid control makes calendars easy.

Your view model should have three classes: Month, Week, and Day. The Week class contains a list of Day objects, and the Month class has a list of Week objects. Each class should have a reference to its parent, i.e. Week in Day and Month in Week.

The Week class's constructor should initialize its Days property to be a list of 7 Day objects. The Month class's constructor has to have more specific logic in it for setting up its Weeks property; I'll leave that to you.

The Day object should expose these properties:

public DayOfWeek DayNumber { get; private set; }
public ObservableCollection<SchoolclassCode> Codes { get { return Week.Codes; } }

as well as a read/write string Code property that does property-change notification.

The Week object should expose:

public ObservableCollection<SchoolclassCode> Codes { get { return Month.Codes; } }
public IEnumerable<Day> Days { get; private set; }
public IEnumerable<string> Codes
{
   get { return Days.Select(x => x.Code); }
}

You can then define a data template for presenting days:

<DataTemplate DataType="{x:Type local:Day}">
   <Grid>
      <Grid.ColumnDefinitions>
         <ColumnDefinition SharedSizeGroup="Sunday"/>
         <ColumnDefinition SharedSizeGroup="Monday"/>
         <ColumnDefinition SharedSizeGroup="Tuesday"/>
         <ColumnDefinition SharedSizeGroup="Wednesday"/>
         <ColumnDefinition SharedSizeGroup="Thursday"/>
         <ColumnDefinition SharedSizeGroup="Friday"/>
         <ColumnDefinition SharedSizeGroup="Saturday"/>
      </Grid.ColumnDefinitions>
      <ComboBox 
         Grid.Column="{Binding DayNumber}" 
         ItemsSource="{Binding Codes}" 
         SelectedValue="{Binding Code, Mode=TwoWay}"/>
   </Grid>
</DataTemplate>

and weeks:

<DataTemplate DataType="{x:Type Week}">
  <ItemsControl ItemsSource="{Binding Days}">
    <ItemsControl.ItemTemplate>
      <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal"/>
      </ItemsPanelTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>
</DataTemplate>

and months:

<DataTemplate DataType="{x:Type Month}">
  <ItemsControl ItemsSource="{Binding Weeks}" Grid.IsSharedSizeScope="True">
    <ItemsControl.ItemTemplate>
      <ItemsPanelTemplate>
        <StackPanel Orientation="Vertical"/>
      </ItemsPanelTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>
</DataTemplate>

Adding day names and week numbers to the above templates is straightforward.

Robert Rossney
@msfanboy - If you are doing a calendar (fixed number of weeks) then this is definitely the way to go. Based on the graphic you showed, I assumed you needed an undetermined number of rows (and used a grid accordingly).
Joseph Sturtevant
nono guys I do not do a calendar its a TimeTable which is setup ONE time in a lesson planner wizard control. After the user choosed from the combobox the schoolclasscode for example perioud 3 at monday and he continues with the wizard I will read all "Rows" from the datagrid aka itemSource collection and create the lesson planner with sql insert statements.
msfanboy
@JosephAbout why I am using a DataGrid: Well before I had the idea with the Comboboxes in the cell my user aka my wife ;-) selected the schoolclasscode from a combobox outside the datagrid and then did a doubleclick on a cell to assign the schoolclasscode to the item behind the clicked cell. The workflow was not so good as you have to go back and forward to the combobox always choosing the new schoolclasscode you want to assign. Assigning is only possible with a Selector control with a SelectedItem Property and DataGrid has that feature.ItemsControl do not has that feature.
msfanboy
@RobertYou get my idea? But thank you for all that code because now I got a new idea that I can at least get rid of the DataGrid for the timetable, because I do not longer need the SelectedItem as my user sets the Schoolclasscode within the combobox. That will give me a smaller .exe ;-)Back to my question: Can someone please check my last question concerning mvvm in the initial post, please?
msfanboy
@Robertah what I am talking today lol of course I need a DataGrid not because of the SelectedItem, but only a DataGrid has a RowHeader to display the number of the lesson and even show it fixed when the user scrolls down. ItemsControl do not has that feature of a RowHeader. Yes I know I could put a int lessonnumber into my model and bind that to the first column and put whole ItemControl into a scrollviewer...
msfanboy
@JosephIn my datatemplated UserControl.Resources I put this:<ViewModel:WizardPageTimeTableWeekAViewModel x:Key="TimeTableID" />but this will not work as WizardPageTimeTableWeekAViewModel has not an EMPTY Constructor !!!My Binding in the WizardPageTimeTableWeekView.xamlItemsSource="{Binding SchoolclassCodes, Source={StaticResource ResourceKey=TimeTableID}}"grrrr...
msfanboy
@Josephok I have it working after some trys:http://img80.imageshack.us/img80/9825/timetable.png :)<UserControl.Resources><CollectionViewSource x:Key="ClassCodes" Source="{Binding SchoolclassCodes}" /> </UserControl.Resources><DataTemplate><ComboBox DisplayMemberPath="SchoolclassName"SelectedValue="{Binding SchoolclassCodeMonday}" ItemsSource="{Binding Source={StaticResource ClassCodes}}"IsSynchronizedWithCurrentItem="False"/></DataTemplate>instead of using the Key on my ViewModel whats not possible as the Ctor is not parameterless I use a CollectionViewSource. Thats neat!
msfanboy
It's simple to add a header to this design; just add a Grid containing Labels for each column to the Month template.
Robert Rossney