简体   繁体   English

WPF文本框未使用数据绑定,iNotifyPropertyChanged和PropertyChanged触发器更新

[英]WPF TextBox Not Updating with Data Binding, iNotifyPropertyChanged, and PropertyChanged Trigger

I am having a binding issue I wasn't able to figure out for the past two days. 我遇到了过去两天都无法解决的具有约束力的问题。 I have thoroughly went through most of the relevant threads on SO, and I still wasn't able to pinpoint where my error lies. 我已经彻底了解了SO上的大多数相关线程,但仍然无法查明我的错误所在。

The issue I'm having is with one of the textboxes in my program. 我遇到的问题是程序中的一个文本框。 The purpose of it is to show the file the user has selected from the file browser. 其目的是显示用户从文件浏览器中选择的文件。 I have bound the text property of it to a string called parameterFileSelected but the textbox never updates even though debugging seems to be showing that the iNotifyPropertyChanged is called and executed properly. 我已将其text属性绑定到名为parameterFileSelected的字符串,但即使调试似乎表明iNotifyPropertyChanged已被调用并正确执行,该文本框也从未更新。

Please help me take a look at my code below if there are any mistakes in my code. 如果我的代码有任何错误,请帮助我在下面查看我的代码。

The textbox is part of an xaml called GenerateReports and this view is tied to the GenerateReportsViewModel as follows: 该文本框是一个名为GenerateReports的xaml的一部分,该视图与GenerateReportsViewModel绑定如下:

Code for setting datacontext to GenerateReportsViewModel 用于将datacontext设置为GenerateReportsViewModel的代码

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

        <Grid.ColumnDefinitions>
        ....

Code for TextBox. 文本框的代码。 I have tried removing the Twoway mode, changing it to Oneway and removing the mode but there is no difference. 我尝试删除双向模式,将其更改为单向并删除模式,但是没有区别。

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

To get the file browser and then to pass the selected file result to the GenerateReportsViewModel, this is the function in the codebehind file. 要获取文件浏览器,然后将选定的文件结果传递给GenerateReportsViewModel,这是代码隐藏文件中的函数。 The genviewmodel is initialized in the beginning of the codebehind file as GenerateReportsViewModel genViewModel = new 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());
      }
}

This is the code that's called in GenerateReportsViewModel to update the parameterFileSelected string the textbox is bound to. 这是在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;
        }
}

Here is the ViewModelBase the viewmodel is attached to. 这是viewmodel附加到的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));
        }
    }
}

EDIT Working Solution after Applying Kevin's Suggestions 应用凯文的建议后编辑工作解决方案

For simplicity sake, the Datacontext was set in the XAML. 为了简单起见,在XAML中设置了Datacontext。

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

Then, I call the string the textbox was bound to, in the viewmodel directly from code behind. 然后,我直接在后面的代码中在viewmodel中调用文本框绑定到的字符串。

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

The ViewModel now uses Kevin's ViewModelBase: 现在,ViewModel使用Kevin的ViewModelBase:

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

Thank you Kevin for your solution. 谢谢凯文的解决方案。 Now my 2-day-long problem is solved. 现在,我为期2天的问题已解决。

I found out that my previous ViewModelBase was calling iNotifyPropertyChanged but somehow when the View was updated, the value was null instead. 我发现以前的ViewModelBase调用了iNotifyPropertyChanged,但是以某种方式在更新View时,该值为null。

I'm trying to understand why using the ref keyword in your viewModel. 我试图理解为什么在您的viewModel中使用ref关键字。 I learned a nice way to create the BaseViewModel from the Classon and Baxter book which you can find below. 我从Classon and Baxter本书中学到了一种创建BaseViewModel的好方法,您可以在下面找到。 The view-model implements the INotifyPropertyChanged like you did. 视图模型像您一样实现INotifyPropertyChanged。 What you did with [CallerMemberName] is great, it's really magical the way we can reference to our properties thanks to it. 您对[CallerMemberName]所做的事情很棒,这真是一种神奇的方式,我们可以借助它来引用属性。

The view model uses a the dictionary to store its properties. 视图模型使用字典来存储其属性。 It uses a pretty neat trick of looking through the dictionnary keys to see if we contain the string name of the property.Otherwise, we will return a default T value. 它使用了一个很好的技巧来浏览字典键,以查看是否包含该属性的字符串名称,否则将返回默认的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);
            }
        }
    }

As for your GenerateReportViewModel, with the common view model that I provided you, your class then becomes : 至于您的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;
            }

         }

Oh before I forgot, I don't know if it was your intention, but your GenerateReportViewModel is private. 哦,在我忘记之前,我不知道这是否是您的意图,但是您的GenerateReportViewModel是私有的。 This has some impact on your code. 这会对您的代码产生一些影响。 Don't forget that by defaut, classes are private! 不要忘记,默认情况下,班级是私人的!

As for your code behind, even though it could be consider bad practice, I recommend that you have a private field (OpenFileDialog _openFileDialog)that you construct while initializing your page. 至于后面的代码,即使它可能被认为是不好的做法,我还是建议您在初始化页面时构造一个私有字段(OpenFileDialog _openFileDialog)。 Because doing it each time your clicking your button is going to consume more data that you need your application to. 因为每次单击按钮都将这样做,因此会消耗更多您的应用程序所需的数据。

//EDIT I have review my code,and it seemed that the property was not programmed correctly. //编辑我检查了我的代码,似乎该属性未正确编程。 public class GenerateReportsViewModel : CommonViewModelBase { 公共类GenerateReportsViewModel:CommonViewModelBase {

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

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

             }

More about my comment about constructing the page and binding the view model. 关于我关于构建页面和绑定视图模型的评论的更多信息。 While creating your page, you have to create the view-model for that page and then bind it to the data context. 创建页面时,必须为该页面创建视图模型,然后将其绑定到数据上下文。 I don't know what you do in your code, but I could provide with this sample such as 我不知道您在代码中做什么,但是我可以提供此示例,例如

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM