3
votes

I'm trying to bind a bunch of user controls to a WPF application. The idea is the viewmodel holds the list of server names, passes those to an ObservableCollection, and then the collection is bound to the main window.

When in design mode, everything works just fine! However, when I run the application, the binding seems to break down, and I'm at a loss to explain why.

enter image description here

This is the XAML for the user control:

<UserControl x:Class="ServerMonitor.Wpf.ServerControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ServerMonitor.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <local:Server />
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="TextAlignment" Value="Center" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
            </Style>
        </Grid.Resources>

        <TextBlock Grid.Row="0" Text="{Binding Name}" />

        <TextBlock Grid.Row="8" Text="{Binding LastPolled}" />
    </Grid>
</UserControl>

This is the view model (ViewModelBase is an abstract class that implements INotifyPropertyChanged):

public class MainWindowViewModel : ViewModelBase {

    private List<string> _MachineNames = new List<string> {
        "wschampad",
        // Test
        "T009", "T010", "T011", "T012",
    };
    public List<string> MachineNames {
        get { return _MachineNames; }
        set { _MachineNames = value; OnPropertyChanged("ManchineNames"); }
    }

    private ObservableCollection<Server> _Servers;
    public ObservableCollection<Server> Servers {
        get {
            if (_Servers == null) {
                _Servers = new ObservableCollection<Server>();
                foreach (var machine in MachineNames) {
                    _Servers.Add(new Server {
                        Name = machine, 
                        LastPolled = DateTime.Now, 
                    });
                }
                OnPropertyChanged("Servers");
            }

            return _Servers;
        }
        set { _Servers = value; OnPropertyChanged("Servers"); }
    }
}

And this is the main window XAML:

<Window x:Class="ServerMonitor.Wpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ServerMonitor.Wpf"
        Title="Leading Hedge Server Monitor" Height="350" Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Menu Grid.Row="0">
            <MenuItem Header="_File">
                <MenuItem Header="_Exit" Name="MenuFileExit" Click="MenuFileExit_Click" />

            </MenuItem>
            <MenuItem Header="_Edit">
                <MenuItem Header="_Options" Name="MenuEditOptions" IsEnabled="False" />
            </MenuItem>
            <MenuItem Header="_Help">
                <MenuItem Header="_About" Name="MenuHelpAbout" IsEnabled="False" />
            </MenuItem>
        </Menu>

        <ItemsControl Grid.Row="1" ItemsSource="{Binding Servers}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Black" BorderThickness="1" Margin="5,5,5,5">
                        <local:ServerControl DataContext="{Binding}" />
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

    </Grid>
</Window>

EDIT

Changing the DataContext within the UserControl to ignorable worked to correct the issue!

<UserControl x:Class="ServerMonitor.Wpf.ServerControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ServerMonitor.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <d:UserControl.DataContext>
        <local:Server />
    </d:UserControl.DataContext>
1

1 Answers

3
votes

Get rid of this in your UserControl:

<UserControl.DataContext>
    <local:Server />
</UserControl.DataContext>

This is overwriting your DataContext you're passing to it from the ItemTemplate, and you end up with default values for DateTime and string.