winforms 通过与TextBox(Winform)进行双向通信,在PropertyChangedEvent上出现NullReferenceException异常

ca1c2owp  于 2022-11-17  发布在  其他
关注(0)|答案(1)|浏览(119)

错误消息:
HResult= 0x 80004003消息=对象引用未设置为对象示例。源=System.Windows.Forms堆栈跟踪:在系统.Windows.窗体.绑定.SetPropValue(对象值)在系统.Windows.窗体.绑定.PushData(布尔值强制)在系统.Windows.窗体.绑定管理器库.PushData(布尔值成功)(& S)
在系统.组件模型.属性描述符. OnValueChanged(对象组件,事件参数e)在系统.组件模型.反射属性描述符.OnValueChanged(对象组件,事件参数e)
第132行:在项目XYZ.MVVM.视图模型. RecipeViewModel.通知属性已更改(字符串属性名称)
此异常最初在此调用堆栈中引发:[外部代码]项目XYZ.MVVM.视图模型. RecipeViewModel. cs中的通知属性已更改(字符串)
编辑:第132行:PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
目的:
在WinForm中显示一个配方编辑器,您可以在其中选择一个类别,然后选择要更改的配方,并使用一些嵌套属性对象(烘箱温度和时间斜坡等)层/步骤更改配方。
用途:
1.在显示的ComboBox 1中选择类别
1.在显示的组合框中选择配方2
所选的配方应显示在一些文本框、组合框中,一些特定步骤显示在数据网格视图中。此外,如果您在组合框2中选择另一个配方,它应可以更改且不会丢失其信息。
问题:参见上述异常。
下面是代码的一部分(我希望我没有忘记C&P和翻译中的一些内容):
我有一个CollectionViewSource RecipesOnWork,我用CategoryFilter过滤它,效果很好。我把这个过滤后的视图给了一个组合框RecipesOnWork.Cast<RecipeViewModel>().ToList(),在那里我可以按名称选择一个食谱。我找不到更好的方法...(如果有一个,我想读一下,😉但不是针对这个问题)
我还在事件CollectionViewSource.CurrentChanged上挂接了我自己:RecipesOnWork.View.CurrentChanged += CurrentRecipeChanged;
使用从ComboBox2.SelectedIndexChanged事件中选择的配方,我检查配方是否在RecipesOnWork中,在RecipesOnWork中查找配方,在CurrentItem上设置以下内容:

if(sender is ComboBox combobox)
        {
            combobox.SelectedItem is RecipeViewModel Recipe
            if (RecipesOnWork.View.Contains(Recipe))
            { RecipesOnWork.View.MoveCurrentTo(Recipe); }
        }

由于CurrentRecipeChanged,我设置了我的私有CurrentSelectedRecipe,因为我的

private RecipeViewModel curentSelectedRecipe = new RecipeViewModel();

    public RecipeViewModel CurentSelectedRecipe
    {
        get { return CurentSelectedRecipe; }
        set 
        {
            // Edit:
            if (curentSelectedRecipe == value || value == null)
            { return; }
            //Even if I deactivate this part of unsubcribe I get the exception.
            if (curentSelectedRecipe != null)
            { curentSelectedRecipe.PropertyChanged -= InformRecipeChanged; }
            curentSelectedRecipe = value;
            //Even if I deactivate this part of subcribe I get the exception.
            curentSelectedRecipe.PropertyChanged += InformRecipeChanged;
        }
    }

并将更改通知给所有可能的视图。(Views是视图的列表)

Views.ForEach(view => view.OnNext(Recipe));} 

public override void OnNext(RecipeViewModel value)
        {
            ViewModel = value;
            // Work fine in the test:
            ClearSubscribe(FRM.TBNestedOBJ1Time, "Text", ViewModel.nestedOBJ1, nameof(NestedClassViewModel1.Time), true, OnPropertyChanged);
            // If I'm changing the text here I get a System.NullReferenceException:
            ClearSubscribe(FRM.TBRecipeName, "Text", ViewModel, nameof(RecipeViewModel.Name), true, OnPropertyChanged);
        }

        private void ClearSubscribe(Control control, string binderPropertie, object obj, string objPropertie, bool formatEnabling, DataSourceUpdateMode updateMode)
        {
            control.DataBindings.Clear();
            control.DataBindings.Add(binderPropertie, obj, objPropertie, formatEnabling, updateMode);
}

嵌套OBJ 1也具有INotify属性更改

public class RecipeViewModel : INotifyPropertyChanged
{

    //Example for one nested class NestedOBJ1:
    private NestedClassViewModel1 nestedOBJ1 = new NestedClassViewModel1();
    public NestedClassViewModel1 NestedOBJ1
    {
        get => nestedOBJ1;
        set
        {
            if (nestedOBJ1 == value)
            { return; }
            nestedOBJ1 = value;
            NotifyPropertyChanged();
        }
    }

    private string name = "Undefined";
    public string Name
    {
        get => name;
        set
        {
            if (name == value)
            { return; }
            name = value;
            NotifyPropertyChanged();
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

**我必须使用.Net Framework 4.8和WinForm。**我正尝试通过一点调整来实现MVVM模式,所以我决定使用MVC和MVVM的混合模式...=〉我有控制器、视图模型、视图和模型,其中包含一些业务逻辑,我在这些逻辑之间给予对象。因此,也许我还丢失了PropertyChanged?

如果我一步一步地改变Recipe.Name,我可以看到,该Recipe.Name已经有了新的值,此时它也不是空的。

  • 只有EventHandler PropertyChanged可以为空?我不明白为什么在检查它是否为空时会出现NullReferenceException?(PropertyChanged?.Invoke()
  • 这是因为在第一次检查中它不是null,并且因为我的ClearSubscribe()我得到了null异常吗?
  • 如果我想进行双向交流,我如何才能避免这种情况?
  • 为什么它在嵌套对象上没有崩溃?

我肯定我错过了什么,我不明白。也许有人可以帮助我?
我试图解决这个问题,因为5或6天,所以我不知道如何才能做到这一点,也许做得更好了。
谢谢你的帮助。
到目前为止,我尝试的还有:若要手动将它系结至OnNext方法中的文字方块,请透过

FRM.CategoryName.TextChanged -= ManualBound;
    FRM.CategoryName.Text = ViewModel.Rubrik;
    FRM.CategoryName.TextChanged += ManualBound;

若为手动绑定:

private void ManualBound(object sender, EventArgs e)
{
    if (sender is TextBox box)
    {
        ViewModel.Category = box.Text;
    }
}

这是可行的,但我必须做很多工作,这就是为什么我使用ClearSubscribe(Control control, string binderPropertie, object obj, string objPropertie, bool formatEnabling, DataSourceUpdateMode updateMode),这对我来说是一个“更聪明”的方式后,一些研究绑定一些属性的TextBox
要查找问题:
关于public RecipeViewModel CurentSelectedRecipe中@Selvin编辑的建议

set 
{
    if (curentSelectedRecipe == value || value == null)
    { return; }
    //Even if I deactivate this part of unsubcribe I get the exception.
    if (curentSelectedRecipe != null)
    { curentSelectedRecipe.PropertyChanged -= InformRecipeChanged; }
    curentSelectedRecipe = value;
    //Even if I deactivate this part of subcribe I get the exception.
    curentSelectedRecipe.PropertyChanged += InformRecipeChanged;
}
if (curentSelectedRecipe == value ) 
{ return; }

我添加了这个,但仍然得到了这个System.NullReferenceException
在NotifyPropertyChanged方法中://没有帮助:如果(属性更改== null){ return;}

if (PropertyChanged!= null)
    {
        foreach (PropertyChangedEventHandler subscriber in PropertyChanged?.GetInvocationList())
        {
            if (subscriber != null)
            {
                Debug.WriteLine(subscriber.Target.ToString());
                    /*
                    Output: 
                    System.ComponentModel.ReflectPropertyDescriptor
                    System.ComponentModel.ReflectPropertyDescriptor
                    System.ComponentModel.ReflectPropertyDescriptor
                    */
            }
        }
        
    }

    var myEvent = new PropertyChangedEventArgs(propertyName); // was not null
    var this2 = this;// was not null
    // still throw Exception.
    PropertyChanged?.Invoke(this2, myEvent);
jqjz2hbq

jqjz2hbq1#

在@Selvin的评论之后,我的思想走上了正确的思考道路,所以我想我发现了自己的错误:

public override void OnNext(RecipeViewModel value)
{
    if (value == null || ViewModel == value)
    { return; }
    //...
}

|| ViewModel ==值是重要的部分。

因为所有的东西都连接好了,所以不需要重新连接任何东西--〉所以不要在那里中断。
我的错误是我问:

  • 这是因为在第一次检查中它不是null,并且因为我的ClearSubscribe()我得到了null异常吗?

我不知道要避免这种情况,所以我尝试了上面的代码,它成功了。所以问题是改变了PropertyChanged,而它在自己的调用中改变了它,在那里它变成了null,这就是我通过NullCheck的原因。((this, new PropertyChangedEventArgs(propertyName));

相关问题