1

The Model

OCLMEditorModel // The main model
    OCLMEditorModelData // main data read from XML file (using XMLSerializer)
        List<Meeting> Meetings // List of Meeting objects

Amongst other things, the Meeting object includes:

Meeting
    Date
    SpecialEvent

Where SpecialEvent is:

SpecialEvent
    Description
    Location
    Type

The ModelView

OCLMEditorViewModel // This inherits from INotifyPropertyChanged
    ObservableCollection Meetings // Using the OCLMEditorModel.OCLMEditorModelData.Meetings list as a source
    Meeting // The current meeting object

    _Model = new OCLMEditorModel();
    _MeetingsList = new ObservableCollection<Data.MeetingInfo.Meeting>(_Model.Meetings);
    _Meeting = _Model.MeetingForThisWeek(); // Starts us off correctly (right item in the Meetings list)

The View

This contains a ComboBox of the Meetings collection:

<ComboBox x:Name="comboMeetingWeek" ItemsSource="{Binding Meetings}"
    SelectedItem="{Binding Meeting, UpdateSourceTrigger=PropertyChanged}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Image Source="Images/Bell.png" Margin="0,0,5,0"
                       Visibility="{Binding IsSpecialEvent, Converter={StaticResource BoolToHiddenConverter}}" Stretch="None"/>
                <TextBlock Text="{Binding DateMeetingAsText}" />
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

So the source is the ObservableCollection. And the SelectedItem is bound to:

public Data.MeetingInfo.Meeting Meeting
{
    get { return _Meeting; }
    set
    {
        // Has the existing meeting object changed at all?
        if(_Meeting != null && _Meeting.IsDirty)
        {
            // Yes, so save it
            _Model.Serialize();
            _Meeting.MarkClean();
        }

        // Now we can update to new value
        if (value != null)
        {
            _Meeting = value;
            OnPropertyChanged("Meeting");
        }
    }
}
private Data.MeetingInfo.Meeting _Meeting;

As far as I am aware this is a correct use of MVVM. But now I want to go a step further. I have this Image that I am going to use for a ContextMenu:

<Image Grid.Column="1" HorizontalAlignment="Right" Source="Images\event_time.png" Margin="2">
    <Image.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Set Special Event" Command="{Binding SetSpecialEventCommand, Mode=OneWay}">
                <MenuItem.Style>
                    <Style TargetType="MenuItem">
                        <Setter Property="IsEnabled" Value="True"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Meeting.IsSpecialEvent}" Value="True">
                                <Setter Property="IsEnabled" Value="False"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </MenuItem.Style>
            </MenuItem>
            <MenuItem Header="Remove Special Event">
                <MenuItem.Style>
                    <Style TargetType="MenuItem">
                        <Setter Property="IsEnabled" Value="False"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Meeting.IsSpecialEvent}" Value="True">
                                <Setter Property="IsEnabled" Value="True"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </MenuItem.Style>
            </MenuItem>
        </ContextMenu>
    </Image.ContextMenu>
</Image>

The question ... We have the Meeting object that is currently selected in the ComboBox ...

If I right-click and choose Set Special Event I wan tto show a popup window. This is where I geta bit confused. I understand that I must use "UpdateSourceTrigger=Explicit" inside the popup window and then in the OK button perform the update (special event description, location, type).

I have a problem with the popup window. Xaml:

<Window x:Class="OCLMEditor.SpecialEventWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:OCLMEditor"
        mc:Ignorable="d"
        Title="SpecialEventWindow" Height="300" Width="300">
    <Grid>
        <TextBox x:Name="textDescription" HorizontalAlignment="Left" Height="23" Margin="38,49,0,0" TextWrapping="Wrap"/>
    </Grid>
</Window>

_SetSpecialEventCommand = new DelegateCommand<string>(
    (s) =>
    {
        SpecialEventWindow windowEvent = new SpecialEventWindow();
        windowEvent.DataContext = _Meeting.SpecialEvent;
        windowEvent.ShowDialog();
    },
    (s) => true);

Yet, in the XAML editor for the popup window it thinks it has no DataContext. So it will not let me bind the text box to one of the properties.

Update

I am not sure about the comment that was raised as they clearly missed my code that sets the DataContext. But it is easy to miss. It is there. :)

_SetSpecialEventCommand = new DelegateCommand<string>(
    (s) =>
    {
        SpecialEventWindow windowEvent = new SpecialEventWindow();
        windowEvent.DataContext = _Meeting.SpecialEvent;
        windowEvent.ShowDialog();
    },
    (s) => true);

It does work. But what I am getting at is that when I set the datacontext in code, the XAML editor doesn't seem to know about it. So when I click on controls and go to bind... it doesn't show up.

DataContext

My MainWindow has:

MainWindow

So it shows the active data context and I can use the IDE to navigate through bindings. But not on this popup window. I have to do it all manually in the code editor.

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164

1 Answers1

3

You are giving your popup window a DataContext at runtime, but not at design time. The IDE isn't smart enough to figure out what you want it to do by analyzing your code.

The reason your main window has a DataContext at design time is because you're giving it one declaratively in the XAML, which doesn't require any deep insight on the part of the IDE to figure out what your desires are; you're just setting a property, in stuff that does happen at design time:

<Window.DataContext>
    <local:OCLMEditorViewModel />
</Window.DataContext>

That initializes your main window's DataContext property with an instance of OCLMEditorViewModel, and that is the property you're seeing in the Properties next to the "New" button. You're not seeing that in your popup window because you didn't do that in the XAML for your popup window. That's because you're setting its DataContext at runtime with a specific, pre-existing instance of SpecialEvent.

There are two ways to get what you want here: First, the "right" way, which is to use the d:DataContext ignorable attribute:

<Window
    ...
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance Type=local:SpecialEvent, IsDesignTimeCreatable=True}"
    ...
    >

This requires SpecialEvent to have a default constructor. It can have special "mock data" initialization when it detects that it's been instantiated at design time (or not).

Even simpler (but not as nice), in your case you can just instantiate it the same way you did in your main window:

<Window.DataContext>
    <local:SpecialEvent />
</Window.DataContext>

That'll get you a typed DataContext at design time. At runtime, your command that shows the dialog will replace that junk instance with the specific instance it gets from _Meeting.SpecialEvent, since all the XAML stuff is done by the time the Window's constructor completes.

Community
  • 1
  • 1