当前位置: 首页 > 知识库问答 >
问题:

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

相弘方
2023-03-14

我有一个约束性问题,在过去的两天里我一直无法解决。我已经彻底地浏览了关于SO的大部分相关线程,但仍然无法确定错误所在。

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

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

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

用于将datacontext设置为GenerateReportsViewModel的代码

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

        <Grid.ColumnDefinitions>
        ....

TextBox的代码。我尝试过删除双向模式,将其更改为单向模式并删除该模式,但没有区别。

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

要获取文件浏览器,然后将选定的文件结果传递给GenerateReportsViewModel,这是codebehind文件中的函数。genviewmodel在codebehind文件的开头初始化为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中调用的代码,用于更新文本框绑定到的参数File选择字符串。

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

应用Kevin的建议后编辑工作html" target="_blank">解决方案

为了简单起见,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。


共有1个答案

白赞
2023-03-14

我试图理解为什么在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;
}
 类似资料:
  • 最后是我的XAML代码 我已经尝试将绑定模式设置为twoway和其他绑定设置,但行为没有任何变化。我想做的是弄清楚如何让drop功能像手动键入框并离开框一样工作,而不必绕过绑定并直接设置属性。

  • 本文向大家介绍wpf 实现INotifyPropertyChanged,包括了wpf 实现INotifyPropertyChanged的使用技巧和注意事项,需要的朋友参考一下 示例 INotifyPropertyChanged是绑定源(即DataContext)使用的接口,以使用户界面或其他组件知道属性已更改。WPF在看到PropertyChanged事件引发时会自动为您更新UI 。最好在所有视图

  • 我是C#新手,我不断收到以下无法删除的错误。 我的代码如下,我不能确切地说出哪一行导致了错误,但我怀疑它与RibbonRadioButtons有关,因为如果我删除它们,我不会得到错误。只有在单击两个或更多单选按钮后,错误才会出现。ComboBoxItem上的一个回答继续抛出绑定错误,尽管style暗示是多个Refresh()语句导致了这个问题,但我不知道如何避免这个问题。 有人能帮我解决这个问题吗

  • 问题内容: 我有一个绑定到我的EventTracker bean的JTable,本质上是一个列表的包装,我将仅将其用作附加/清除(即,简单的日志)。问题是,当我将条目添加到列表中并尝试触发事件时,我看不到任何更改。我正在使用NetBeans IDE。 EventTracker bean被添加到视图中并实例化为eventTracker1。从那里,我右键单击表格,然后选择“表格内容…”。表模型已绑定到

  • 我已经审阅了Microsoft提供的关于触发器的文档。[https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob-trigger?tabs=python][1] 事实上,在Azure函数中使用参数允许我们检索blob和一些属性(),我们还可以使用函数读取字节,但是我们如何将字节转换为

  • 使用Datagridtemplatecolumn将WPFDatagrid绑定到组合框。很难获得组合框绑定的selectedItem。我发现了类似的例子,但这并不能解决我的问题。 请在下面找到我的 XAML 的代码片段和数据结构: 我在上面定义了一个数据结构,它实现了INotifyPropertychanged接口。 现在,在视图模型中,有一个可观察的X列表集合,即XList,它绑定到XAML中的数