[英]Update visibility of grid from another thread
问题:
我有以下课程:
1)SMServiceClient-RestSharp的包装器
public class SMServiceClient
{
public delegate void AuthorizationSucceededHandler(SMServiceEventArgs args);
public event AuthorizationSucceededHandler AuthorizationSucceeded;
public delegate void AuthorizationFailedHandler(SMServiceEventArgs args);
public event AuthorizationFailedHandler AuthorizationFailed;
public delegate void RequestStartedHandler(SMServiceEventArgs args);
public event RequestStartedHandler RequestStarted;
public delegate void RequestFinishedHandler(SMServiceEventArgs args);
public event RequestFinishedHandler RequestFinished;
private const string baseUrl = "http://10.0.0.6:4000";
private RestClient client;
public SMServiceClient()
{
client = new RestClient(baseUrl);
client.CookieContainer = new CookieContainer();
client.FollowRedirects = false;
}
public void AuthUser(string username, string password)
{
RequestStarted(new SMServiceEventArgs());
var request = new RestRequest("/api/login", Method.POST);
request.AddParameter("username", username);
request.AddParameter("password", password);
var response = client.Execute<User>(request);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
AuthorizationFailed(new SMServiceEventArgs("Credential are incorrect!"));
}
else {
AuthorizationSucceeded(new SMServiceEventArgs(response.Data));
}
RequestFinished(new SMServiceEventArgs());
}
}
2)ViewModel-用于存储接收到的数据和绑定的类
public class ViewModel : INotifyPropertyChanged
{
private SMServiceClient _client;
public SMServiceClient client { get { return _client; } }
private Credentials _currentLogging;
public Credentials currentLogging
{
get { return _currentLogging; }
set
{
if (_currentLogging != value)
{
_currentLogging = value;
OnPropertyChanged(new PropertyChangedEventArgs("currentLogging"));
}
}
}
public ViewModel()
{
_client = new SMServiceClient();
currentLogging = new Credentials();
}
public void AuthUser()
{
_client.AuthUser(currentLogging.login, currentLogging.password);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
}
3)MainWindow.xaml.cs-应用程序的主窗口
public partial class MainWindow : MetroWindow
{
public ViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new ViewModel();
_viewModel.client.AuthorizationSucceeded += ServiceClient_AuthorizationSucceeded;
_viewModel.client.AuthorizationFailed += ServiceClient_AuthorizationFailed;
_viewModel.client.RequestStarted += ServiceClient_RequestStarted;
_viewModel.client.RequestFinished += ServiceClient_RequestFinished;
this.DataContext = _viewModel;
}
void ServiceClient_RequestStarted(SMServiceEventArgs args)
{
this.Dispatcher.BeginInvoke(new Action(() => { overlayThrobber.Visibility = System.Windows.Visibility.Visible; }), DispatcherPriority.Normal);
}
void ServiceClient_RequestFinished(SMServiceEventArgs args)
{
this.Dispatcher.BeginInvoke(new Action(() => { overlayThrobber.Visibility = System.Windows.Visibility.Collapsed; }), DispatcherPriority.Normal);
}
void ServiceClient_AuthorizationSucceeded(SMServiceEventArgs args)
{
_viewModel.loggedUser = args.loggedUser;
Storyboard storyboard = new Storyboard();
TimeSpan duration = new TimeSpan(0, 0, 0, 0, 600);
DoubleAnimation animation = new DoubleAnimation();
animation.From = 1.0;
animation.To = 0.0;
animation.Duration = new Duration(duration);
Storyboard.SetTargetName(animation, "overlayControl");
Storyboard.SetTargetProperty(animation, new PropertyPath(Control.OpacityProperty));
storyboard.Children.Add(animation);
storyboard.Completed += storyboard_Completed;
storyboard.Begin(this);
}
void ServiceClient_AuthorizationFailed(SMServiceEventArgs args)
{
loginErrorBlock.Text = args.message;
}
private void LoginForm_LoginButtonClicked(object sender, RoutedEventArgs e)
{
_viewModel.AuthUser();
}
void storyboard_Completed(object sender, EventArgs e)
{
overlayControl.Visibility = System.Windows.Visibility.Collapsed;
}
}
和Xaml:
<controls:MetroWindow x:Class="ScheduleManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sm="clr-namespace:ScheduleManager"
xmlns:controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:extWpf="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
Title="Schedule Manager" Height="500" Width="600" MinHeight="500" MinWidth="600">
<Grid x:Name="mainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="overlayThrobber" Grid.Column="1" Grid.Row="1" Background="Gray" Opacity="0.7" Panel.ZIndex="2000" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<controls:ProgressRing x:Name="processThrobber" Grid.Column="1" Grid.Row="1" Foreground="White" IsActive="True" Opacity="1.0"/>
</Grid>
</Grid>
</controls:MetroWindow>
整个过程从调用LoginForm_LoginButtonClicked开始。
我想在SMServiceClient发送数据请求时显示overlayThrobber 。 我尝试了很多方法来异步显示它,例如通过Dispatcher.Invoke和Dispatcher.BeginInvoke或创建新的Thread,但没有任何效果。 我需要从RequestStarted和RequestFinished事件的处理程序中显示/隐藏overlayThrobber 。 也许它不起作用,因为我试图调用在SMServiceClient的处理程序中显示overlayThrobber的功能? 你能给我什么建议?
罗马,
我通常在这里建议在视图模型上使用一个属性,该属性连接“ overlayThrobber”元素的可见性。 然后,您可以更改此值,并且UI将自动更新-这使用的是称为MVVM(模型-视图-视图模型)的标准XAML范例。
“视图”是XAML。 它使用数据绑定来绑定到ViewModel(在本例中为您的代码)。 模型通常是后端,在这种情况下,您可以将通过网络检索的数据视为模型。
因此,在这种情况下,您将在视图模型上公开一个属性,我们将其称为ThrobberVisible ...
私人布尔值_throbberVisible;
public bool ThrobberVisible
{
get { return _throbberVisible; }
set
{
if (value != _throbberVisible)
{
_throbberVisible = value;
this.OnPropertyChanged("ThrobberVisible");
}
}
}
这还取决于您还在View Model类上创建OnPropertyChanged方法...
protected void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (null != handler)
handler(this, new PropertyChangedEventArgs(propertyName));
}
此代码引发属性更改通知,这是UI会监听的内容。 有许多UI框架可以实现这种玩法。 我使用了一堆好伴侣和XAML专家Josh的片段。
现在,您需要更改XAML,以便将rob动者的可见性与ThrobberVisible值绑定在一起。 通常,您将执行以下操作...
... Visibility={Binding ThrobberVisible, Converter={StaticResource boolToVisible}}
这将可见性与视图模型上的属性挂钩。 然后,你需要一个转换器从布尔真/假值转换为可见XAML /折叠的价值,比如这个类。 您将定义一个静态资源作为此布尔转换器,通常在App.Xaml文件中执行此操作,因为我在各处都使用此转换器。
一切就绪之后,您现在只需更改视图模型上的布尔值,就可以翻转UI元素的可见性。
希望这可以帮助! 抱歉,如果代码无法编译,我只是徒手输入。 :-)
您所做的一切都是正确的,但是您使用Threads Dispatcher而不是UI Dispatcher来更新UI。 您可以执行以下操作以从另一个线程更新UI。 这不仅与可视性有关,如果要从另一个线程执行任何类型的UI操作,也必须执行相同的操作。 在您的MainWindow.Xmal.cs中声明一个如下所示的本地调度程序并使用它
public partial class MainWindow : MetroWindow
{
public ViewModel _viewModel;
private Dispatcher dispathcer = Dispatcher.CurrentDispatcher;
public MainWindow()
{
InitializeComponent();
_viewModel = new ViewModel();
_viewModel.client.AuthorizationSucceeded += ServiceClient_AuthorizationSucceeded;
_viewModel.client.AuthorizationFailed += ServiceClient_AuthorizationFailed;
_viewModel.client.RequestStarted += ServiceClient_RequestStarted;
_viewModel.client.RequestFinished += ServiceClient_RequestFinished;
this.DataContext = _viewModel;
}
void ServiceClient_RequestStarted(SMServiceEventArgs args)
{
dispathcer.Invoke(new Action(() => { overlayThrobber.Visibility = System.Windows.Visibility.Visible; }), DispatcherPriority.Normal);
}
void ServiceClient_RequestFinished(SMServiceEventArgs args)
{
dispathcer.Invoke(new Action(() => { overlayThrobber.Visibility = System.Windows.Visibility.Collapsed; }), DispatcherPriority.Normal);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.