WPF DataTemplate multibinding causes FrameworkElement.ApplyTemplate() to throw NullReferenceException

StackOverflow https://stackoverflow.com/questions/19552433

سؤال

To give some context : I have a list of items, and i draw an image for each of them. They all have a black border but the one that is bound to the same object than a given "Brush" property. That one has a red border to show which brush is selected.

here is the code

<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Border BorderBrush="Black"
                BorderThickness="2 2 2 2">
            <Border.Style>
                <Style TargetType="{x:Type Border}">
                    <Style.Triggers>
                        <DataTrigger Value="True">
                            <DataTrigger.Binding>
                                <MultiBinding Converter="{StaticResource ResourceKey=AreEqualConverter}">
                                    <Binding/>
                                    <Binding ElementName="Me" Path="Brush"/>
                                </MultiBinding>
                            </DataTrigger.Binding>
                            <Setter Property="BorderBrush" Value="Red"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Border.Style>
            <Image Source="{Binding}" 
                   Height="{Binding ElementName=TilePreviewSize, Path=Value}"
                   Width="{Binding ElementName=TilePreviewSize, Path=Value}"
                   RenderOptions.BitmapScalingMode="NearestNeighbor"
                   MouseDown="OnBrushMouseDown"/>
        </Border>
    </DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

I get the follow error "NullReferenceException : Object reference not set to an instance of an object." when the following block of code is uncommented. Can't figure out what is the problem.

<Style TargetType="{x:Type Border}">
    <Style.Triggers>
        <DataTrigger Value="True">
            <DataTrigger.Binding>
                <MultiBinding Converter="{StaticResource ResourceKey=AreEqualConverter}">
                    <Binding/>
                    <Binding ElementName="Me" Path="Brush"/>
                </MultiBinding>
            </DataTrigger.Binding>
            <Setter Property="BorderBrush" Value="Red"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

If you want to see the converter's code, here it is (quite simple)

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    if (values.Length < 2)
        throw new NotSupportedException();

    var obj = values[0];

    for (int i = 1; i < values.Length; ++i)
        if (!values[i].Equals(obj))
            return false;

    return true;
}

Here is the stacktrace or the error:

   at IntegratorUI.Contexts.RoomContext.System.Windows.Markup.IStyleConnector.Connect(Int32 connectionId, Object target) in [...] line 138
   at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)
   at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlObjectWriter objectWriter)
   at System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(DependencyObject container, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)
   at System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren)
   at System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)
   at System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)
   at System.Windows.FrameworkElement.ApplyTemplate()
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.StackPanel.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run()
   at IntegratorUI.App.Main() in D:\trollmeme\integrator\IntegratorUI\obj\Debug\App.g.cs:line 0
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Thanks for helping me sort this out!

Nic

هل كانت مفيدة؟

المحلول 2

Your problem is that you've defined your handler for MouseDown="OnBrushMouseDown"; inside of a DataTemplate. It therefore cannot retrieve the MouseButtonEventHandler properly.

To fix the issue, add a new Style in your resource dictionary of the style :

<Style x:Key="MouseDownHandlerStyle" TargetType="{x:Type Image}">
    <EventSetter Event="MouseDown" Handler="OnBrushMouseDown"/>
</Style>

Cheers!

نصائح أخرى

Some of the values in the array can be null. Use the static Equals method. It might also be prudent to check that obj is not null.

if (!Object.Equals(values[i], obj))

Update

After seeing your stack trace, it seems like your problem is related to this issue. It seems that when you use nested templates/styles. This affects .NET 4, and seems to be fixed in .NET 4.5. The suggested workaround is to reference the style as a resource.

<UserControl.Resources>
   <Style x:Key="MyBorder" TargetType="{x:Type Border}" ...
</UserControl.Resources>

<DataTemplate>
    <Border BorderBrush="Black"
            BorderThickness="2 2 2 2"
            Style="{StaticResource MyBorder}" ...
</DataTemplate>

Note that if your entire ItemsControl is a nested inside another template, you should probably move all those templates to resources.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top