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

时间:2015-01-16 01:56:17

标签: c# wpf data-binding textbox viewmodel

我有一个我在过去两天无法弄清楚的具有约束力的问题。我已经完全了解了SO上的大多数相关主题,但我仍然无法确定我的错误所在。

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

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

文本框是名为GenerateReports的xaml的一部分,该视图与GenerateReportsViewModel绑定如下:

将datacontext设置为GenerateReportsViewModel的代码

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

        <Grid.ColumnDefinitions>
        ....

TextBox的代码。我已经尝试删除Twoway模式,将其更改为Oneway并删除模式但没有区别。

<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;
        }
}

这是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));
        }
    }
}

修改 应用凯文的建议后的工作解决方案

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

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

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

 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); }
        }
}

谢谢Kevin的解决方案。现在我的2天问题已经解决了。

我发现我之前的ViewModelBase正在调用iNotifyPropertyChanged,但不知何故,当View更新时,值为null。

1 个答案:

答案 0 :(得分: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是私有的。这对您的代码有一些影响。不要忘记,通过defaut,课程是私人的!

至于你的代码背后,即使它可能被认为是不好的做法,我建议你有一个私有字段(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;
}