WPF बम बग: स्थिर संसाधन में इवेंट सेटर दो बार सेट किया जा रहा है, दूसरी बार रिक्त करने के लिए



visual-studio-2013 staticresource (0)

अगर मैं एक्सएमएल में सेटरबेज ऑब्जेक्ट का संग्रह संग्रहीत करने की कोशिश करता हूं, जिसमें शामिल है और इवेंटसेटर, एक्सएमएल लोडर एक त्रुटि फेंकता है

मूल कारण यह है कि xaml लोडर दोबारा PresentationFramework.dll! System.Windows.EventSetters.Event सेट करने का प्रयास करता है: पहली बार सही मान (ButtonBase.Click RoutedEvent) पर, लेकिन दूसरी बार रिक्त करने के लिए, और यह एक अपवाद फेंकता है मेरी संलग्न संपत्ति कॉलबैक शामिल नहीं है।

ईवेंट को दो बार दोबारा जोड़ने का प्रयास क्यों करता है और दूसरी बार क्यों नल है? मैंने जाँच की कि इस्तेमाल किया जाने वाला सीटीआर डिफॉल्ट है, इवेंटसीटर किसी भी असामान्य तरीके से संग्रह के साथ इंटरैक्ट नहीं कर रहा है, इसलिए ऐसा नहीं है। वास्तविक कारण WPF में एक बग है जो एक घटना (ईवेंट और इवेंटहान्डलर) के दो-भाग संरचना को पार्स करने की चुनौती को हल करता है।

राय

<Window x:Class="Spec.Plain.MTCMinimal"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
        Title="MTCMinimal" Height="300" Width="300">

<Window.Resources>

    <SetterBaseCollection x:Key="ButtonStyleSetters">
        <Setter Property="FrameworkElement.Height" Value="30"></Setter>
        <EventSetter Event="ButtonBase.Click" Handler="StyleClick" />
    </SetterBaseCollection>

</Window.Resources>

<Button Name="Button1"
        local:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />

पीछे कोड केवल इनिशियलाइज़ कंबल और ईवेंट हैंडलर के लिए एक स्टब है। InitializeComponent के दौरान त्रुटि उत्पन्न होती है।

व्यवहार

public static readonly DependencyProperty StyleSettersProperty =
    DependencyProperty.RegisterAttached(
        "StyleSetters", typeof(MyStyleSetters),
        typeof(Behaviours),
        new PropertyMetadata(default(MyStyleSetters),
            ButtonSettersChanged));

private static void ButtonSettersChanged (DependencyObject d,
    DependencyPropertyChangedEventArgs args)
{
    var fe = d as FrameworkElement;
    if (fe == null) return;
    var ui = d as UIElement;

    var newValue = args.NewValue as MyStyleSetters;
    if (newValue != null)
    {
        foreach (var member in newValue)
        {
            var setter = member as Setter;
            if(setter != null)
            {
                fe.SetValue(setter.Property, setter.Value);
                continue;
            }
            var eventSetter = member as EventSetter;
            if (eventSetter == null) continue;
            if (ui == null || eventSetter.Event == null) continue;
            ui.AddHandler(eventSetter.Event, eventSetter.Handler);
        }
    }
}

public static void SetStyleSetters(DependencyObject element,
    MyStyleSetters value)
{
    element.SetValue(StyleSettersProperty, value);
}

public static MyStyleSetters GetStyleSetters (
    DependencyObject element)
{
    return (MyStyleSetters)element
        .GetValue(StyleSettersProperty);
}

त्रुटि

System.Windows.Markup.XamlParseException occurred
  _HResult=-2146233087
  _message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '11' and line position '26'.
  HResult=-2146233087
  IsTransient=false
  Message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '11' and line position '26'.
  Source=PresentationFramework
  LineNumber=11
  LinePosition=26
  StackTrace:
       at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
  InnerException: System.ArgumentNullException
       _HResult=-2147467261
       _message=Value cannot be null.
       HResult=-2147467261
       IsTransient=false
       Message=Value cannot be null.
Parameter name: value
       Source=PresentationFramework
       ParamName=value
       StackTrace:
            at System.Windows.EventSetter.set_Event(RoutedEvent value)
       InnerException

डिबगिंग

मैं System.Windows.EventSetter.Event पर फ़ंक्शन ब्रेकपॉइंट को सेटर को पास किए गए मान लॉग इन करने के लिए एक कार्य के साथ सेट करता हूं ...

तो मैं ऐप चलाता हूं और आउटपुट विंडो को देखता हूं और यह देख सकता हूं कि सेटर दो बार मारा गया था, पहली बार सही मान से, दूसरी बार मूल्य शून्य के साथ ...

काम का उदाहरण इस गिटब रिपो में समाधान में पाया जा सकता है जिसे प्रोजेक्ट में EventSetterNull-SO-41604891-2670182

baml

XamlNodeList के सूचकांक सदस्य में एक बीपी सेट करके मैं SetterBaseCollection xaml ऑब्जेक्ट से जुड़े xaml प्रतीकों को पकड़ सकता है ...

XamlNode [0] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [1] "StartObject: SetterBaseCollection"
XamlNode [2] "StartMember: _Items"
XamlNode [3] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [4] "StartObject: Setter"
XamlNode [5] "StartMember: Property"
XamlNode [6] "Value: Height"
XamlNode [7] "EndMember: "
XamlNode [8] "StartMember: Value"
XamlNode [9] "Value: 30"
XamlNode [10] "EndMember: "
XamlNode [11] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [12] "EndObject: "
XamlNode [13] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [14] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [15] "StartObject: EventSetter"
XamlNode [16] "StartMember: Event"
XamlNode [17] "Value: System.Windows.Baml2006.TypeConverterMarkupExtension"
XamlNode [18] "EndMember: "
               -->EventSetter value: {System.Windows.RoutedEvent}
XamlNode [19] "None: LineInfo: System.Xaml.LineInfo"
XamlNode [20] "StartMember: Event"
XamlNode [21] {System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Xaml.XamlNode.ToString() in ...\AppData\Local\JetBrains\Shared\v06\DecompilerCache\...\XamlNode.cs:line 159
   at <>x.<>m0(XamlNode& <>4__this)}
XamlNode [22] "EndMember: "
               -->EventSetter value: null
               !!!Then the null reference error throws
XamlNode [23] = "StartMember: Handler"
XamlNode [24] = "Value: StyleClick"
XamlNode [25] = "EndMember: "
XamlNode [26] = "None: LineInfo: System.Xaml.LineInfo"
XamlNode [27] = "EndObject: "
XamlNode [28] = "EndMember: "
XamlNode [29] = "EndObject: "
XamlNode [30] = "None: "
                 The remaining of the 41 nodes are all "None: "

बग?

बैमल नोडलिस्ट अजीब लग रहा है, सबसे पहले इडक्स [20] से शुरू होने वाला एक अतिरिक्त इवेंट सदस्य है और यह सदस्य वास्तव में एक सिस्टम है।
यह XamlObjectWriter को दिया जा रहा है जो कि इवेंटसेटर गुणक के पास जाता है और यह त्रुटि का कारण है।
बाम तब अपेक्षित रूप में किया जाता है, हेन्डलर सदस्य दिखा रहा है और सदस्यों और ऑब्जेक्ट्स को ठीक से समाप्त कर दिया गया है।

निष्कर्ष

समस्या एक्सएमएल से बैमल तक रूपांतरण में है, इसलिए मैं कहूंगा कि यह एक बग है एक परिहार्य बढ़त के मामले में हालांकि

काम के आसपास

शैली में घटना को सेट करने की कोशिश करने के बजाय, किसी अभिमुख वस्तु को किसी मूल ऑब्जेक्ट में उपयोग करें। उदाहरण के लिए बटनबैस.क्लिक करें = "स्टाइलक्लिक" एक स्टैकपैनेल में व्यवहार को सब कुछ क्लिक करता है जो कि मैं मूल रूप से करने की कोशिश कर रहा था। संपत्ति सेटर्स का संग्रह अभी भी एक स्थिर संसाधन में सेट किया जा सकता है और सम्बद्ध संपत्ति-आधारित व्यवहारों द्वारा भस्म हो सकता है।

मूल कारणों पर आगे शोध

समस्या यह है कि एक घटना संपत्ति के पास दो तत्व हैं: ईवेंट और हैंडलर जब बामल 2006 रीडर बैल में एक ऑब्जेक्ट को पार्स करता है, तो उसे इसकी संरचना के लिए अनुमति देने की आवश्यकता होती है ताकि सुनिश्चित हो सके कि यह ऑब्जेक्ट सदस्यों की ईमानदारी से व्याख्या करने के लिए सही स्थिति में है। ऐसा करने के लिए, इसकी एक राज्य मशीन है, जो कि ReadObject में एक समय की लूप से प्रेरित होती है , जिसे प्रोसेस्_ऑनबैमलरेकॉर्ड कहा जाता है। यह विधि अगले xamlNodeType को डीकोड करती है और इसे पार्स करने के लिए उपयुक्त विधि को कॉल करती है और इसे ऑब्जेक्ट के रूप में लिखता है। इन विधियों में से एक को प्रोसेस_Property कहा जाता है और इसके पास बामल में इवेंट कॉम्प्लेक्स को नियंत्रित करने के लिए इसमें विशेष लॉजिक हार्ड-वायर्ड है।

समस्या यह है कि, यदि घटना बाम में एक Process_PropertyWithConverter के रूप में दर्ज की जाती है , तो यह विधि किसी घटना के लिए विशेष आवश्यकताओं के बारे में जानकारी नहीं है और सबकुछ सामानों को भरता है ईवेंट हैंडलर एक प्रॉपर्टी टैग के साथ प्रीफिक्स किया गया है (सबसे अधिक संभावना है कि ईवेंट पार्सर को इस उप-संरचना के लिए एक ही वाक्यविन्यास का उपयोग करना था) और क्योंकि कोई एंडमेल, स्टार्टमेम्बर राज्य परिवर्तन नहीं हुआ है, हैंडलर उप-संपदा द्वारा व्याख्या की जाती है इवेंट संपत्ति के रूप में ReadObject और इवेंट सेटटर ऑब्जेक्ट को बनाया जा रहा है, एक त्रुटि फेंकता क्योंकि यह इवेंट गुण पहले से सेट है