【问题标题】:WPF TextBox Not Updating with Data Binding, iNotifyPropertyChanged, and PropertyChanged TriggerWPF 文本框未使用数据绑定、iNotifyPropertyChanged 和 PropertyChanged 触发器进行更新
【发布时间】:2015-03-14 14:21:10
【问题描述】:

我遇到了过去两天无法解决的绑定问题。我已经彻底浏览了 SO 上的大部分相关线程,但我仍然无法确定我的错误所在。

我遇到的问题是程序中的一个文本框。它的目的是显示用户从文件浏览器中选择的文件。我已将它的文本属性绑定到一个名为 parameterFileSelected 的字符串,但即使调试似乎显示 iNotifyPropertyChanged 已正确调用和执行,文本框也不会更新。

如果我的代码有任何错误,请帮我看看下面的代码。

文本框是名为 GenerateReports 的 xaml 的一部分,此视图与 GenerateReportsViewModel 相关联,如下所示:

将 datacontext 设置为 GenerateReportsViewModel 的代码

<Grid >
        <Grid.DataContext>
            <vm:GenerateReportsViewModel/>
        </Grid.DataContext>

        <Grid.ColumnDefinitions>
        ....

文本框的代码。我曾尝试删除双向模式,将其更改为单向并删除模式,但没有区别。

<TextBox Grid.Column="2" Grid.Row="1" Margin="5" Text="{Binding parameterFileSelected, Mode=Twoway, UpdateSourceTrigger=PropertyChanged}" ></TextBox>

获取文件浏览器,然后将选择的文件结果传递给 GenerateReportsViewModel,这是代码隐藏文件中的函数。 genviewmodel 在代码隐藏文件的开头被初始化为GenerateReportsViewModel genViewModel = new GenerateReportsViewModel();

private void ParaFileButtonClick(object sender, RoutedEventArgs e)
{

      OpenFileDialog openFileDialog = new OpenFileDialog();              
      if (openFileDialog.ShowDialog() == true)                
      {
          DataContext = genViewModel;
          genViewModel.updateParameterFileSelected(openFileDialog.FileName.ToString());
      }
}

这是在 GenerateReportsViewModel 中调用的代码,用于更新文本框绑定到的 parameterFileSelected 字符串。

class GenerateReportsViewModel : ViewModelBase
{ 

        private string _parameterFileSelected;
        public string parameterFileSelected
        {
            get { return _parameterFileSelected; }
            set { SetValue(ref _parameterFileSelected, value); }
        }

        public void updateParameterFileSelected(string parameterFile)
        {
            parameterFileSelected = parameterFile;
        }
}

这是视图模型附加到的 ViewModelBase。

public class ViewModelBase : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        public void SetValue<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
        {
            if (property != null)
            {
                if (property.Equals(value)) return;
            }

            OnPropertyChanged(propertyName);
            property = value;
        }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

编辑 应用凯文的建议后的工作解决方案

为简单起见,Datacontext 在 XAML 中设置。

<Grid>
        <Grid.DataContext>
            <vm:GenerateReportsViewModel x:Name="generateReportsViewModel"/>
        </Grid.DataContext>

然后,我在视图模型中直接从后面的代码中调用文本框绑定的字符串。

 private void ParaFileButtonClick(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();            
            if (openFileDialog.ShowDialog() == true)
            {
                generateReportsViewModel.parameterFileSelected = openFileDialog.FileName.ToString();
            }
        }

ViewModel 现在使用 Kevin 的 ViewModelBase:

public class GenerateReportsViewModel : ViewModelBase
    {
public string parameterFileSelected
        {
            get { return this.GetValue<string>(); }
            set { this.SetValue(value); }
        }
}

感谢凯文的解决方案。现在我为期 2 天的问题解决了。

我发现我以前的 ViewModelBase 正在调用 iNotifyPropertyChanged,但不知何故,当视图更新时,该值改为 null。

【问题讨论】:

    标签: c# wpf data-binding textbox viewmodel


    【解决方案1】:

    我试图了解为什么在您的 viewModel 中使用 ref 关键字。我从 Classon 和 Baxter 的书中学到了一种创建 BaseViewModel 的好方法,您可以在下面找到。视图模型像您一样实现了 INotifyPropertyChanged。你对 [CallerMemberName] 所做的很棒,多亏了它,我们可以引用我们的属性的方式真的很神奇。

    视图模型使用字典来存储其属性。它使用了一个非常巧妙的技巧,即通过字典键查看我们是否包含属性的字符串名称。否则,我们将返回一个默认的 T 值。

     public class CommonBaseViewModel: INotifyPropertyChanged
      {
            private Dictionary<string, object> Values { get; set; }
    
            protected CommonBaseViewModel()
            {
                this.Values = new Dictionary<string, object>();
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected T GetValue<T>([CallerMemberName] string name=null)
            {
                if (this.Values.ContainsKey(name))
                {
                    return (T)this.Values[name];
                }
                else
                {
                    return default(T);
                }
            }
    
            protected void SetValue(object value, [CallerMemberName] string name =  null)
            {
                this.Values[name] = value;
    
                //notify my property
                this.OnPropertyChanged(new PropertyChangedEventArgs(name));
    
            }
    
            protected void OnPropertyChanged([CallerMemberName] string name=null)
            {
                this.OnPropertyChanged(new PropertyChangedEventArgs(name));
            }
    
            protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
            {
                if(this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, e);
                }
            }
        }
    

    至于你的 GenerateReportViewModel,使用我提供给你的通用视图模型,你的类就变成了:

       public class GenerateReportsViewModel : CommonViewModelBase
            { 
    
               private string _parameterFileSelected;
                public string parameterFileSelected
                {
                    get { return _parameterFileSelected; }
                    set { SetValue(ref _parameterFileSelected, value); }
                }
                   get
                {
                    return this.GetValue<string>();
                }
                set
                {
                    this.SetValue(value);
                }
    
                public void updateParameterFileSelected(string parameterFile)
                {
                    parameterFileSelected = parameterFile;
                }
    
             }
    

    哦,在我忘记之前,我不知道这是否是您的意图,但您的 GenerateReportViewModel 是私有的。这对您的代码有一些影响。不要忘记,默认情况下,课程是私有的!

    至于您的代码,即使它可能被认为是不好的做法,我建议您在初始化页面时构建一个私有字段 (OpenFileDialog _openFileDialog)。因为每次单击按钮都会消耗更多数据,而您需要应用程序来处理。

    //编辑 我查看了我的代码,似乎该属性没有正确编程。 公共类 GenerateReportsViewModel : CommonViewModelBase {

                   private string _parameterFileSelected;
                    public string parameterFileSelected
                    {
                        get
                            {
                                return this.GetValue<string>();
                            }
                        set
                            {
                               this.SetValue(value);
                            }
    
                    public void updateParameterFileSelected(string parameterFile)
                    {
                        parameterFileSelected = parameterFile;
                    }
    
                 }
    

    更多关于我关于构建页面和绑定视图模型的评论。创建页面时,您必须为该页面创建视图模型,然后将其绑定到数据上下文。 我不知道您在代码中做了什么,但我可以提供此示例,例如

    public GenerateReportView()
    {
       InitializeComponent();
      //Some operations
      var generateReportViewModel  = new GenerateReportViewModel();
     this.DataContext = generateReportViewModel;
    }
    

    【讨论】:

    • 您好,感谢您的回复。我现在已将我的视图模型设为公共类,并使用您提供的代码更新了我的视图模型库。文本框绑定目前仍然不起作用。我有另一个可观察的集合绑定到我的视图模型中的列表视图,并且它与您的视图模型库以及我以前的视图模型库一起按预期工作。知道我可以做些什么来尝试解决问题吗?再次感谢:)
    • 当您构建页面时,我发现您实际上并没有在页面创建后立即绑定您的 viewModel !你想要做的是在你的按钮事件中绑定viewModel
    • 我不完全确定是否已为 viewModel 正确设置了 datacontext,但在视图的 xaml 中,我已绑定到网格中的 viewModel &lt;Grid &gt; &lt;Grid.DataContext&gt; &lt;vm:GenerateReportsViewModel/&gt; &lt;/Grid.DataContext&gt;
    • 我对@9​​87654326@ 的获取和设置已经与您在编辑中提供的一样。我在InitializeComponent(); 行之后添加了一行以将DataContext 设置为generateReportViewModel。文本框绑定仍然不适合我。输出窗口中也没有数据绑定错误。真是不知所措:(
    • 哦,对于视图模型的创建,我是这样做的:public partial class GenerateReports : UserControl { GenerateReportsViewModel genViewModel = new GenerateReportsViewModel();
    猜你喜欢
    • 2021-11-27
    • 2015-09-24
    • 1970-01-01
    • 2015-05-22
    • 2013-03-28
    • 1970-01-01
    • 2011-07-29
    • 2011-03-05
    • 1970-01-01
    相关资源
    最近更新 更多