WPF/MVVM setting UserControl.DataContext in XAML r

2019-09-11 05:41发布

问题:

Whenever I paste the UserControl.DataContext portion of the code below, I get "Object reference not set" in the designer. I'm pretty convinced it's something inside the view model, but I think it has to do with binding because it runs fine...

When the view model loads it makes a call to a WCF service method to fetch a short list of object types for something in the database that then allocates a local list that is bound to in the designer.

Is there anyway to nail down what exactly isn't set? I've tried opening another copy of VS and attaching to process on devenv and xdesproc, but when I do, nothing every shows up in the second VS instance. I'm at a loss for how to narrow this down.

<UserControl
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:f="clr-namespace:FocusVMLib;assembly=FocusVMLib"
xmlns:ViewModels="clr-namespace:SalvageTracking.ViewModels"
mc:Ignorable="d"
x:Class="SalvageTracking.Controls.CompanyEditor"
x:Name="UserControl"
d:DesignWidth="514" d:DesignHeight="280.667">

<UserControl.DataContext>
    <ViewModels:CompanyEditorViewModel x:Name="companyEditorViewModel"/>
</UserControl.DataContext>

<Grid x:Name="LayoutRoot" Background="White" Margin="8,8,8,8" FocusManager.FocusedElement="{Binding ElementName=CompanyNameTextBox}">

    <TextBlock VerticalAlignment="Top" Text="Company Name" TextWrapping="Wrap" HorizontalAlignment="Left"/>
    <TextBox x:Name="CompanyNameTextBox" Text="{f:FocusBinding Path=Name, ValidatesOnDataErrors=True}"
             HorizontalAlignment="Left" Height="23" Margin="0,20.96,0,0" TextWrapping="Wrap"
             VerticalAlignment="Top" Width="120"/>

    <TextBlock VerticalAlignment="Top" Text="Company Type" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="125,0,0,0"/>
    <ComboBox SelectedIndex="{f:FocusBinding Path=TypeIndex, ValidatesOnDataErrors=True}"
              ItemsSource="{Binding CompanyTypes}" DisplayMemberPath="Name"
              HorizontalAlignment="Left" Margin="125,20.96,0,0" VerticalAlignment="Top" Width="121.5"/>

    <TextBlock VerticalAlignment="Top" Text="Company City" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="251.5,0,0,0"/>
    <TextBox  Text="{f:FocusBinding Path=City, ValidatesOnDataErrors=True}"
              HorizontalAlignment="Left" Height="23" Margin="251.5,20.96,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>

    <TextBlock VerticalAlignment="Top" Text="Company State" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="376.5,0,0,0"/>
    <ComboBox Text="{f:FocusBinding Path=State, ValidatesOnDataErrors=True}"
              ItemsSource="{Binding States}" DisplayMemberPath="Name"
              HorizontalAlignment="Left" Margin="376.5,20.96,0,0" VerticalAlignment="Top" Width="121.5"/>

    <TextBlock VerticalAlignment="Top" Text="Company Address 1" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="0,48.96,0,0"/>
    <TextBox  Text="{f:FocusBinding Path=Address1, ValidatesOnDataErrors=True}"
              HorizontalAlignment="Left" Height="23" Margin="0,69.92,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="373"/>

    <TextBlock VerticalAlignment="Top" Text="Company Zip Code" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="378,48.96,0,0"/>
    <TextBox  Text="{f:FocusBinding Path=Zip, ValidatesOnDataErrors=True}"
              HorizontalAlignment="Left" Height="23" Margin="378,69.92,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>

    <TextBlock VerticalAlignment="Top" Text="Company Address 2" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="0,97.92,0,0"/>
    <TextBox Text="{f:FocusBinding Path=Address2, ValidatesOnDataErrors=True}"
             HorizontalAlignment="Left" Height="23" Margin="0,118.88,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="498"/>

    <TextBlock VerticalAlignment="Top" Text="Company Phone #" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="0,146.88,0,0"/>
    <TextBox Text="{f:FocusBinding Path=Phone, ValidatesOnDataErrors=True}" HorizontalAlignment="Left" Height="23"
             Margin="0.001,167.84,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>

    <TextBlock VerticalAlignment="Top" Text="Company Fax #" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="125.001,146.88,0,0"/>
    <TextBox Text="{f:FocusBinding Path=Fax, ValidatesOnDataErrors=True}" HorizontalAlignment="Left" Height="23"
             Margin="125.001,167.84,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>

    <TextBlock VerticalAlignment="Top" Text="Company Email" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="250.001,146.88,0,0"/>
    <TextBox Text="{f:FocusBinding Path=Email, ValidatesOnDataErrors=True}"
             HorizontalAlignment="Left" Height="23" Margin="250.001,167.84,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="247.999"/>

    <TextBlock VerticalAlignment="Top" Text="Company URL" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="0,195.84,0,0"/>
    <TextBox Text="{f:FocusBinding Path=Url, ValidatesOnDataErrors=True}"
             HorizontalAlignment="Left" Height="23" Margin="0,216.801,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="498"/>

    <CheckBox IsChecked="{Binding CanPostOnline, Mode=TwoWay}" Content="Can Post Online"
              HorizontalAlignment="Left" Margin="0,247.801,0,0" VerticalAlignment="Top"/>

    <Button Content="Save" Style="{StaticResource GreenButtonStyle}" Command="{Binding SaveCommand}"
            CommandParameter="{Binding ElementName=UserControl}" IsDefault="True"
            HorizontalAlignment="Left" Margin="343,244.801,0,0" VerticalAlignment="Top" Width="75"/>
    <Button Content="Cancel" Style="{StaticResource GrayButtonStyle}" Command="{Binding CancelCommand}"
            CommandParameter="{Binding ElementName=UserControl}" IsCancel="True"
        HorizontalAlignment="Left" Margin="423,244.801,0,0" VerticalAlignment="Top" Width="75"/>

</Grid> 

Edit: I commented out the DataContext setting in the xaml and did this in the code behind to set up the data context and it got rid of the object not set if that gives any clues.

public partial class CompanyEditor : UserControl
{
    public ViewModels.CompanyEditorViewModel companyEditorViewModel = null;
    public CompanyEditor() 
    {
        this.InitializeComponent();

        companyEditorViewModel = new ViewModels.CompanyEditorViewModel();
        this.DataContext = companyEditorViewModel;
    }
}

回答1:

When you set the DataContext in XAML, the designer instantiates the object. Therefore the code in the object's constructor will be executed.

If your constructor does anything fancy which may need to reference another class, or create objects, you will likely get errors, but only in design-time.

There are a few ways you can get around this.

  1. Check your constructors. Ensure that there cannot be a scenario where you will be referencing objects that are null. if (theClassIWant != null) ... for example.
  2. Make use of design-time DataContext. See here
  3. It is possible to work out whether the view is currently in design time. Caliburn Micro has this built in, you can see an example here. That being said, you do not have to use Caliburn Micro, there are other ways.
  4. Setting the DataContext in code-behind is possible. However you will still have the same problem if you add your UserControl to a Window as the UserControl will be instantiated, and by extension, it's DataContext.
  5. My personal favourite, turn off design-time.


标签: wpf mvvm