关于WPF MVVM下,在线程里修改UI绑定值的安全性

ilikeff8 2019-01-04 09:34:01

mc:Ignorable="d" WindowStartupLocation="CenterScreen" Title="MainWindow" Style="{StaticResource BaseWindowStyle}">

<Window.Resources>
<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="MyLightMode">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="api:LightMode"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="MySortingLogStyle">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="e:ESortingLogStyle"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

<valueConverter:ChannelValueConverter x:Key="ChannelValueConverter"/>
<valueConverter:SortingLogStyleValueConverter x:Key="SortingLogStyleValueConverter"/>
</Window.Resources>

<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding ShowMessageBoxRequest}">
<utils:CustomPopupWindowAction CenterOverAssociatedObject="True" IsModal="True">
<prism:PopupWindowAction.WindowContent>
<dlg:ShowMessageUserControl/>
</prism:PopupWindowAction.WindowContent>
</utils:CustomPopupWindowAction>
</prism:InteractionRequestTrigger>

<prism:InteractionRequestTrigger SourceObject="{Binding ProcessSortingUIRequest}">
<ta:ProcessSortingUITriggerAction/>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>

<Grid>
<Viewbox Stretch="Fill" >
<Canvas Height="648" Width="1024">
<Label Content="{Binding CurrentErrorInfo}" Canvas.Top="597" Background="Red" Foreground="White" Width="1013" Height="36">
<Label.Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource ContentAlignmentLabelStyle}">
<Setter Property="Padding" Value="3,3"/>
<Style.Triggers>
<Trigger Property="Content" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="Content" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>


</Canvas>
</TabItem>
</TabControl>
<StackPanel Visibility="{Binding AutoTagSelfCheckingProgressVisibility,UpdateSourceTrigger=PropertyChanged}" Orientation="Horizontal" Background="White" Canvas.Left="1" Canvas.Top="515" Width="1023" Height="82">
<Label Content=" 自检中..." Style="{StaticResource ContentAlignmentLabelStyle}" />
<ProgressBar Height="42" Value="{Binding AutoTagSelfCheckingProgressValue, UpdateSourceTrigger=PropertyChanged}" Width="882" Minimum="0" Maximum="100" Margin="0,20" Foreground="Blue"/>
</StackPanel>
</Canvas>
</Viewbox>
</Grid>
</base:BaseWindow>





...:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
// 是不是invoke已经可以保证线程安全了?
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

string currentErrorInfo;
public string CurrentErrorInfo
{
get
{ return currentErrorInfo; }
set
{
if (currentErrorInfo != value)
{
Interlocked.Exchange(ref currentErrorInfo, value);
OnPropertyChanged(nameof(CurrentErrorInfo));
}
}
}

object autoTagSelfCheckingProgressVisibility = Visibility.Collapsed;
public Visibility AutoTagSelfCheckingProgressVisibility
{
get
{ return (Visibility)autoTagSelfCheckingProgressVisibility; }
set
{
if ((Visibility)autoTagSelfCheckingProgressVisibility != value)
{
Interlocked.Exchange(ref autoTagSelfCheckingProgressVisibility, value);
OnPropertyChanged(nameof(AutoTagSelfCheckingProgressVisibility));
}
}
}


比如
一个是绑定改变label文字的 CurrentErrorInfo
一个是改变进度条控件显示的AutoTagSelfCheckingProgressVisibility


目前都是直接在ViewModel里某个线程里直接修改绑定值了,倒是一直也没出问题,



ThreadPool.QueueUserWorkItem(obj =>
{
CurrentErrorInfo="111";

AutoTagSelfCheckingProgressVisibility = Visibility.Visible;
});


是不是最好还是这样?:


Application.Current.Dispatcher.Invoke((Action)(()=>{
AutoTagSelfCheckingProgressVisibility = Visibility.Visible;
}));


有没有必要?为什么不用也不会出错,报跨线程处理UI的错误



public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
// 是不是invoke已经可以保证线程安全了?
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}


另外,绑定Visibility属性时,如何设置默认值Collapsed,否则程序启动控件会显示,直到绑定值Collapsed生效后消失


public MainWindow()
{
InitializeComponent();

this.ContentRendered += MainWindow_ContentRendered;
}

private void MainWindow_ContentRendered(object sender, EventArgs e)
{
this.DataContext = new MainViewModel();



Visibility="{Binding AutoTagSelfCheckingProgressVisibility,UpdateSourceTrigger=PropertyChanged}"

...全文
2090 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
货郎大叔 2019-02-08
  • 打赏
  • 举报
回复
学习了…………
  • 打赏
  • 举报
回复
AutoTagSelfCheckingProgressVisibility 本来就是一个没有必要出现的 VM 属性。 VM 里边应该是体现各种业务属性,不是为了 UI 而 UI 的属性。所以进度条是否可见,完全可以通过业务逻辑来设计,通过 Convertor 来将业务数据转变为可见性。
  • 打赏
  • 举报
回复
引用 1 楼 ilikeff8 的回复:

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            App.Current?.Dispatcher?.Invoke((Action)(() =>
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }));
        }
还是在这里加了一个,为了PropertyChanged订阅内部触发保险起见
你认为 WPF 的 binding 机制设计有问题?
  • 打赏
  • 举报
回复
引用 8 楼 ilikeff8 的回复:
不是手动去自己赋值,而且想知道binding后,PropertyChanged的内部机制最后是如何实际更新UI属性的,.net源码里不知道是否能看出来
那只是改变 VM 的属性,你又没有操作 UI 控件,纠结 UI 干什么呢? 如果要纠结 Control.Invoke/BeginInvoke,那么你就不要使用你的那个所谓的 MVVM 框架了,说明它设计有问题。它自然是需要处理 control.BeginInvoke/Invoke,跟你完全无关。
ilikeff8 2019-02-01
  • 打赏
  • 举报
回复
现在把这个还是去掉了,多重invoke毫无意义,只要保证调用方式是
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
而不是
PropertyChanged?(this, new PropertyChangedEventArgs(propertyName));
ilikeff8 2019-01-09
  • 打赏
  • 举报
回复
引用 10 楼 exception1992 的回复:
[quote=引用 8 楼 ilikeff8 的回复:]
[quote=引用 7 楼 exception1992 的回复:]
一般使用PropertyChanged就不会进行txtABC.txt="111"这种赋值方式,如果这样,那就不要用binding了,也就不存在view与viewmodel的分离了。


不是手动去自己赋值,而且想知道binding后,PropertyChanged的内部机制最后是如何实际更新UI属性的,.net源码里不知道是否能看出来[/quote]
估计看不出来的[/quote]

那先这样把,加个保险点,性能上也没啥损失
exception92 2019-01-09
  • 打赏
  • 举报
回复
引用 8 楼 ilikeff8 的回复:
[quote=引用 7 楼 exception1992 的回复:]
一般使用PropertyChanged就不会进行txtABC.txt="111"这种赋值方式,如果这样,那就不要用binding了,也就不存在view与viewmodel的分离了。


不是手动去自己赋值,而且想知道binding后,PropertyChanged的内部机制最后是如何实际更新UI属性的,.net源码里不知道是否能看出来[/quote]
估计看不出来的
loveljy_19901114 2019-01-08
  • 打赏
  • 举报
回复
你既然实现了INotifyPropertyChanged,为什么还要用invoke啊, 有线程安全的考虑的话,应该是在cs文件中考虑吧。要不然还考虑什么前后端分离
ilikeff8 2019-01-08
  • 打赏
  • 举报
回复
引用 3 楼 exception1992 的回复:
 object autoTagSelfCheckingProgressVisibility = Visibility.Collapsed;
        public Visibility AutoTagSelfCheckingProgressVisibility
-》为什么要用object而不用Visibility 。
UI控件和属性不同,操作的只是一个WPF中的属性而已,WPF中的属性会映射到对应的.net属性,自然不会出错。如果使用跨线程去操作WPF中的一个Button,比如:
Task.Run(() =>
{
// 操作button
});
会报UI错误,因为button不是由工作者线程创建的。


因为 Interlocked.Exchange原子操作的参数必须是可引用对象,否则报错,我会在多线程里读写这个值

PropertyChanged内部机制不是很了解,网上看过一篇文章试图去从.net源码查看其何时注册订阅和执行改变,但不幸的是那文章太监了,藏的太深作者没往下走了,我就怕PropertyChanged最终执行还是去调用UI控件,类似txtABC.txt="111"
exception92 2019-01-08
  • 打赏
  • 举报
回复
 object autoTagSelfCheckingProgressVisibility = Visibility.Collapsed;
        public Visibility AutoTagSelfCheckingProgressVisibility
-》为什么要用object而不用Visibility 。
UI控件和属性不同,操作的只是一个WPF中的属性而已,WPF中的属性会映射到对应的.net属性,自然不会出错。如果使用跨线程去操作WPF中的一个Button,比如:
Task.Run(() =>
{
// 操作button
});
会报UI错误,因为button不是由工作者线程创建的。
慧眼识狗熊 2019-01-08
  • 打赏
  • 举报
回复
列表绑定observablecollection后也是不能在线程中进行列表的add和remove的。
ilikeff8 2019-01-08
  • 打赏
  • 举报
回复
引用 7 楼 exception1992 的回复:
一般使用PropertyChanged就不会进行txtABC.txt="111"这种赋值方式,如果这样,那就不要用binding了,也就不存在view与viewmodel的分离了。


不是手动去自己赋值,而且想知道binding后,PropertyChanged的内部机制最后是如何实际更新UI属性的,.net源码里不知道是否能看出来
exception92 2019-01-08
  • 打赏
  • 举报
回复
引用 4 楼 ilikeff8 的回复:
[quote=引用 3 楼 exception1992 的回复:]
 object autoTagSelfCheckingProgressVisibility = Visibility.Collapsed;
        public Visibility AutoTagSelfCheckingProgressVisibility
-》为什么要用object而不用Visibility 。
UI控件和属性不同,操作的只是一个WPF中的属性而已,WPF中的属性会映射到对应的.net属性,自然不会出错。如果使用跨线程去操作WPF中的一个Button,比如:
Task.Run(() =>
{
// 操作button
});
会报UI错误,因为button不是由工作者线程创建的。


因为 Interlocked.Exchange原子操作的参数必须是可引用对象,否则报错,我会在多线程里读写这个值

PropertyChanged内部机制不是很了解,网上看过一篇文章试图去从.net源码查看其何时注册订阅和执行改变,但不幸的是那文章太监了,藏的太深作者没往下走了,我就怕PropertyChanged最终执行还是去调用UI控件,类似txtABC.txt="111"[/quote]
一般使用PropertyChanged就不会进行txtABC.txt="111"这种赋值方式,如果这样,那就不要用binding了,也就不存在view与viewmodel的分离了。
龍月 2019-01-07
  • 打赏
  • 举报
回复
MainViewModel 里面初始化 Visibility 属性就行了。
ilikeff8 2019-01-07
  • 打赏
  • 举报
回复

public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
App.Current?.Dispatcher?.Invoke((Action)(() =>
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}));
}


还是在这里加了一个,为了PropertyChanged订阅内部触发保险起见

110,499

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧