TextBlock不使用ViewModel进行更新 [英] TextBlock not updating using ViewModel
问题描述
我有一个带有TextBlock的WPF窗口(MainWindow.xaml),它显示来自其他UserControl(SampleUserControl.xaml)的结果。我将一个String值(你选择颜色红色。)传递给我的ViewModel(ColorVM.vb)来更新我的WPF窗口中的TextBlock。问题是TextBlock不显示我的UserControl的值。以下是我的示例VB WPF项目的代码:
MainWindow.xaml
< 窗口 x:类 = MainWindow
xmlns = http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x = http://schemas.microsoft.com / winfx / 2006 / xaml
标题 = Window1 高度 = 300 宽度 = 300 >
< 网格 >
< TextBlock x:名称 = StatusIndicatorTextBlock 文字 = {绑定MainWindowStatusIndicator } / >
< / Grid >
< / Window >
SampleUserControl.xaml
< < span class =code-leadattribute> UserControl x:Class = SampleUserControl
xmlns = http://schemas.microsoft.com/winfx/2006/xaml/presentation
< span class =code-attribute> xmlns:x = http: //schemas.microsoft.com/winfx/2006/xaml\"
xmlns:mc = http://schemas.openxmlformats .org / markup-compatibility / 2006
xmlns:d = http://schemas.microsoft.com/expression/blend/2008
mc:可忽略 = d
d:DesignHeight = 300 < span class =code-attribute> d:DesignWidth = 300 >
< 网格 >
< < span class =code-leadattribute>按钮 x:名称 = Button1 点击 = Button1_Click / >
< / Grid >
< / UserControl >
ColorVM.vb
Imports System.ComponentModel
公共 类 ColorVM
实现 INotifyPropertyChanged
私有 _Results 作为 字符串
公共 Sub 新()
我 ._ Results = _Results
结束 Sub
公开 属性结果作为 字符串
获取
返回 我 ._结果
结束 获取
设置( ByVal ResultsValue As String )
Me ._ Results = ResultsValue
Me .NotifyPropertyChanged( MainWindowStatusIndicator)
结束 设置
结束 属性
公开 事件 PropertyChanged(发件人作为 对象,e 作为 PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
私有 Sub NotifyPropertyChanged( ByVal propertyName 作为 字符串)
RaiseEvent PropertyChanged( Me ,新 PropertyChangedEventArgs (propertyName))
结束 Sub
结束 类
SampleUserControl.vb
公共 类 SampleUserControl
私有 Sub Button1_Click(发件人 As 对象,e As RoutedEventArgs)句柄 Button1.Click
Dim ColorVM As New ColorVM()
颜色VM.Results = 您选择红色。
结束 Sub
结束 班级
希望你能解决我的问题,因为我是WPF开发的新手:)
第一个问题
您绑定到名为MainWindowStatusIndicator <的属性/ code>;你为一个名为
MainWindowStatusIndicator
的属性提出PropertyChanged
事件但你的属性实际上称为结果
。
如果查看调试在Visual Studio中输出窗口,您将看到绑定错误,告诉您viewmodel不包含名为MainWindowStatusIndicator
的属性。
将属性名称更改为MainWindowStatusIndicator
,或更改NotifyPropertyChanged
调用正确的属性名称。
第二个问题
你的Button1_Click
方法创建一个viewmodel类的新实例,在该新实例上设置一个属性,然后抛出它。
更改为类的一个实例上的实例属性不会影响该类的任何其他实例。
您有两种选择:
- 从控件的
中检索viewmodel类的当前实例DataContext
属性,并更改该实例上的属性:
私有 Sub Button1_Click(发件人作为 对象, e As RoutedEventArgs)句柄 Button1.Click
Dim context As ColorVM = TryCast (DataContext,ColorVM)
如果 context IsNot Nothing 然后
context.MainWindowStatusIndicator = ...
结束 如果
结束 Sub
- 以MVVM方式执行 - 删除click事件处理程序,并使用
ICommand
相反:
你需要一个ICommand
实现,它会在你的viewmodel上调用一个函数。有多种选择 - 这只是Google搜索中的随机选项:
' 来自以下博文的代码:
' http://www.paulspatterson.com/mvvm-and-wpf-for-vb-net-%E2%80%93-part-5-%E2%80%93-delegating-commands/
' 如果您有自己的版本,请随意使用。 :)
公共 类 DelegateCommand: Implements ICommand
Private m_canExecute As Func( Of Object , Boolean )
私有 m_executeAction 作为操作( 对象)
私有 m_canExecuteCache 作为 Boolean
公共 事件 CanExecuteChanged( ByVal 发件人作为 对象, ByVal e As System.EventArgs) Implements ICommand.CanExecuteChanged
公开 Sub 新( ByVal executeAction 作为动作( 对象), ByVal canExecute As Func( 对象,布尔))
我 .m_executeAction = executeAction
我 .m_canExecute = canExecute
结束 Sub
公共 功能该ion CanExecute( ByVal 参数作为 对象)作为 布尔 实施 ICommand .CanExecute
Dim temp As Boolean = m_canExecute(parameter)
如果 m_canExecuteCache<> temp 然后
m_canExecuteCache = temp
RaiseEvent CanExecuteChanged(我,新 EventArgs())
结束 如果
返回 m_canExecuteCache
结束 功能
公开 Sub 执行( ByVal 参数作为 对象)实现 ICommand.Execute
m_executeAction(参数)
结束 < span class =code-keyword> Sub
结束 类
您的viewmodel然后公开ICommand
实现的属性,调用viewmodel上的私有方法,以便在单击按钮时执行操作:
公共 < span class =code-keyword> Class ColorVM: Implements INotifyPropertyChanged
私有 _MainWindowStatusIndicator 作为 字符串
私人 _ChooseColorCommand 作为 ICommand
公共 Sub New ()
Me ._ ChooseColorCommand = < span class =code-keyword>新 DelegateCommand( AddressOf ChooseColo r, AddressOf CanChooseColor)
结束 Sub
公开 ReadOnly 属性 ChooseColorCommand 作为 ICommand
获取
返回 _ChooseColorCommand
结束 获取
结束 属性
公共 属性 MainWindowStatusIndicator 作为 字符串
获取
返回 _MainWindowStatusInd icator
结束 获取
设置( ByVal 值作为 字符串)
_MainWindowStatusIndicator = Value
NotifyPropertyChanged( MainWindowStatusIndicator)
结束 设置
结束 属性
公共 事件 PropertyChanged(发件人作为 对象,e 作为 PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
私有 Sub NotifyPropertyChanged( ByVal propertyName < span class =code-keyword> As String )
RaiseEvent PropertyChanged ( Me ,新 PropertyChangedEventArgs(propertyName))
结束 Sub
私有 Sub ChooseColor(param As Object )
MainWindowStatusIndicator = < span class =code-string> ...
结束 Sub
私人 乐趣ction CanChooseColor(param As Object ) As 布尔
返回 True
结束 功能
结束 类
您的XAML然后绑定到ICommand
实施,并且根本不使用点击
事件:
< UserControl x:Class = SampleUserControl
< span class =code-attribute> xmlns = http://schemas.microsoft.com/ winfx / 2006 / xaml / presentation
xmlns:x = http:/ /schemas.microsoft.com/winfx/2006/xaml\"
xmlns:mc =http://schemas.openxmlformats.org/markup-compatibility/2006
xmlns:d = http://schemas.microsoft.com/expression/blend/2008
mc:可忽略 = d
d:DesignHeight = 300 d:DesignWidth = 300
>
< 网格 >
< 按钮 x:名称 = Button1 命令 = {绑定路径= ChooseColorCommand} / >
< / Grid >
< / UserControl >
I have a WPF Window (MainWindow.xaml) with TextBlock that displays the result from other UserControl (SampleUserControl.xaml). I pass a String value ("You choose color Red.") to my ViewModel (ColorVM.vb) to update my TextBlock in my WPF Window. The problem is that the TextBlock does not display the value from my UserControl. Here is the code of my sample VB WPF project:
MainWindow.xaml
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBlock x:Name="StatusIndicatorTextBlock" Text="{Binding MainWindowStatusIndicator}"/>
</Grid>
</Window>
SampleUserControl.xaml
<UserControl x:Class="SampleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Button x:Name="Button1" Click="Button1_Click"/>
</Grid>
</UserControl>
ColorVM.vb
Imports System.ComponentModel
Public Class ColorVM
Implements INotifyPropertyChanged
Private _Results As String
Public Sub New()
Me._Results = _Results
End Sub
Public Property Results As String
Get
Return Me._Results
End Get
Set(ByVal ResultsValue As String)
Me._Results = ResultsValue
Me.NotifyPropertyChanged("MainWindowStatusIndicator")
End Set
End Property
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
SampleUserControl.vb
Public Class SampleUserControl
Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
Dim ColorVM As New ColorVM()
ColorVM.Results = "You choose color Red."
End Sub
End Class
Hope that you can help me in my problem because I'm a newbie in WPF development :)
First Problem
You're binding to a property calledMainWindowStatusIndicator
; you're raising aPropertyChanged
event for a property calledMainWindowStatusIndicator
; but your property is actually calledResults
.
If you look in the debug output window in Visual Studio, you'll see binding errors telling you that your viewmodel doesn't contain a property calledMainWindowStatusIndicator
.
Either change the name of the property toMainWindowStatusIndicator
, or change the binding and the string in theNotifyPropertyChanged
call to the correct property name.
Second Problem
YourButton1_Click
method creates a new instance of your viewmodel class, sets a property on that new instance, and then throws it away.
A change to an instance property on one instance of a class will not affect any other instance of that class.
You have two options:
- Retrieve the current instance of the viewmodel class from the control's
DataContext
property, and change the property on that instance:
Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click Dim context As ColorVM = TryCast(DataContext, ColorVM) If context IsNot Nothing Then context.MainWindowStatusIndicator = "..." End If End Sub- Do it the MVVM way - remove the click event handler, and use an
ICommand
instead:
You'll need anICommand
implementation which calls a function on your viewmodel. There are various options available - this is just a random one from a Google search:
' Code from the following blog post: ' http://www.paulspatterson.com/mvvm-and-wpf-for-vb-net-%E2%80%93-part-5-%E2%80%93-delegating-commands/ ' Feel free to use your own version if you have one. :) Public Class DelegateCommand : Implements ICommand Private m_canExecute As Func(Of Object, Boolean) Private m_executeAction As Action(Of Object) Private m_canExecuteCache As Boolean Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) Implements ICommand.CanExecuteChanged Public Sub New(ByVal executeAction As Action(Of Object), ByVal canExecute As Func(Of Object, Boolean)) Me.m_executeAction = executeAction Me.m_canExecute = canExecute End Sub Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute Dim temp As Boolean = m_canExecute(parameter) If m_canExecuteCache <> temp Then m_canExecuteCache = temp RaiseEvent CanExecuteChanged(Me, New EventArgs()) End If Return m_canExecuteCache End Function Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute m_executeAction(parameter) End Sub End Class
Your viewmodel then exposes a property for theICommand
implementation, which calls private methods on the viewmodel to perform the action when the button is clicked:
Public Class ColorVM : Implements INotifyPropertyChanged Private _MainWindowStatusIndicator As String Private _ChooseColorCommand As ICommand Public Sub New() Me._ChooseColorCommand = New DelegateCommand(AddressOf ChooseColor, AddressOf CanChooseColor) End Sub Public ReadOnly Property ChooseColorCommand As ICommand Get Return _ChooseColorCommand End Get End Property Public Property MainWindowStatusIndicator As String Get Return _MainWindowStatusIndicator End Get Set(ByVal Value As String) _MainWindowStatusIndicator = Value NotifyPropertyChanged("MainWindowStatusIndicator") End Set End Property Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged Private Sub NotifyPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Private Sub ChooseColor(param As Object) MainWindowStatusIndicator = "..." End Sub Private Function CanChooseColor(param As Object) As Boolean Return True End Function End Class
Your XAML then binds to theICommand
implementation, and doesn't use theClick
event at all:
<UserControl x:Class="SampleUserControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" > <Grid> <Button x:Name="Button1" Command="{Binding Path=ChooseColorCommand}"/> </Grid> </UserControl>
这篇关于TextBlock不使用ViewModel进行更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!