Windows Phone update binding from background thread - c#

In my Windows Phone 8 Application I have Listbox below
<ListBox x:Name="ListBox1" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding snippet.DownloadPercentage}"
TextWrapping="Wrap"
FontFamily="Portable User Interface"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
I am downloading file asyn and would like to give progress percentage to UI like below;
but it does not update UI. It shows always 0 which is initial int value. If I access DownloadPercentage property at main thread
the it updates with no problem.
private void ClientOnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs downloadProgressChangedEventArgs)
{
// not working
item.snippet.DownloadPercentage = progressPercentage;
// not working
Dispatcher.BeginInvoke(delegate {
item.snippet.DownloadPercentage = progressPercentage;
});
// not working
ProgressChangedEventHandler workerProgressChanged = delegate {
item.snippet.DownloadPercentage = progressPercentage;
};
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += workerProgressChanged;
worker.ReportProgress(progressPercentage);
// WORKING!
#region ProgressIndicator
_progressIndicator.Text = string.Format("Downloading ({0}%) {1}", progressPercentage, item.snippet.Title);
_progressIndicator.IsVisible = true;
_progressIndicator.IsIndeterminate = true;
SystemTray.SetProgressIndicator(this, _progressIndicator);
#endregion
}
what can I do?
Solution;
after #DecadeMoon's hint I had to implement INotifyPropertyChanged over Snippet class.
public class Snippet : INotifyPropertyChanged
{
[JsonProperty("downloadPercentage")]
public int DownloadPercentage
{
get { return _downloadPercentage; }
set
{
_downloadPercentage = value;
RaisePropertyChanged("DownloadPercentage");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

The object whose property you have bound to in the view must implement INotifyPropertyChanged. In your case, you're binding to snippet.DownloadPercentage, therefore the snippet class must implement INotifyPropertyChanged and must raise the PropertyChanged event in the setter of the DownloadPercentage property.
You must also make sure that you only modify the DownloadPercentage property from the UI thread, otherwise you'll get an exception if modified from another thread. This is generally done by using the dispatcher:
Dispatcher.BeginInvoke(() =>
{
item.snippet.DownloadPercentage = progressPercentage;
});

Related

Binding list to textbox won't update if ObservableCollection

I'm creating a custom control in WPF. I bind a List<IMyInterface> to a dependency property. This in turn binds again to a ListBox which shows all the items as expected.
I now want to bind 1 item from this list to a Textblock, so I bind the entire list to the textblock. I have a converter in this which extracts the single item I want.
It has worked fine but for a few reasons, I want to use ObservableCollection instead of List
Oddly, when I change a value in my ObservabaleCollection at run time, the value is shown in the ListBox (success) but not in my textblock. The converter is not even hit!
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Errors = new ObservableCollection<IEventDetail>();
this.Errors.CollectionChanged += Errors_CollectionChanged;
var bw = new BackgroundWorker();
bw.DoWork += ((o, e) =>
{
System.Threading.Thread.Sleep(1500);
Dispatcher.Invoke(() =>
{
this.Errors.Add(new MyEvents("example of some detail", "Failed title"));
});
System.Threading.Thread.Sleep(2500);
Dispatcher.Invoke(() =>
{
this.Errors.Add(new MyEvents("Another example", "Failed title 2"));
});
});
bw.RunWorkerAsync();//background worker for testing/debugging only
}
private void Errors_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged("Errors");
}
private ObservableCollection<IEventDetail> _errors;
public ObservableCollection<IEventDetail> Errors
{
get
{
return this._errors;
}
set
{
this._errors = value;
OnPropertyChanged("Errors");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
And the xaml is simply
<local:Notify Events="{Binding Errors}" DockPanel.Dock="Right"/>
As you can see, I've tried to use the CollectionChanged event to then force fire the INotifyPropertyChanged, but it's firing in my Converter yet the ListBox is updating fine (so I know the binding is fine)
This is the UserControls xaml
<TextBlock Text="{Binding Path=Events, RelativeSource={RelativeSource AncestorLevel=1, AncestorType=UserControl}, Mode=Default, Converter={StaticResource MostRecentConverter}}" Grid.Row="0" />
<ListBox ItemsSource="{Binding Path=Events, RelativeSource={RelativeSource AncestorLevel=1,AncestorType=UserControl}, Mode=Default}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding EventTitle}" Style="{StaticResource txtBckRed}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Do I need to do something else?
As mentioned in the comments TextBlock it bound only to Events property change (not its items) so it won't trigger unless new instance of collection is created. Reacting to INotifyCollectionChanged is characteristic to ItemsSource property.
Solution 1
Leave everything as it is at the moment just give TextBlock some name
<TextBlock Text="{Binding ...}" x:Name="myTextBlock"/>
and subscribe to CollectionChanged event inside your UserControl where you manually force binding target to update
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty EventsProperty =
DependencyProperty.Register("Events",
typeof(IEnumerable),
typeof(MyUserControl),
new PropertyMetadata(EventsPropertyChanged));
private static void EventsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyUserControl)d).EventsPropertyChanged(e);
}
private void EventsPropertyChanged(DependencyPropertyChangedEventArgs args)
{
var newCollection = args.NewValue as INotifyCollectionChanged;
if (newCollection != null)
newCollection.CollectionChanged += (s, e) => myTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
}
public IEnumerable Events
{
get { return (IEnumerable)GetValue(EventsProperty); }
set { SetValue(EventsProperty, value); }
}
}
Solution 2
Create your own collection class inherited from ObservableCollection<T> with custom property that would do what your converter does
public class MyObservableCollection<T> : ObservableCollection<T>
{
private string _convertedText;
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
this.ConvertedText = ...; // <- do here what your IValueConverter does
}
public string ConvertedText
{
get { return _convertedText; }
private set
{
_convertedText = value;
OnPropertyChanged(new PropertyChangedEventArgs("ConvertedText"));
}
}
}
and bind TextBlock.Text to Events.ConvertedText property instead, without need for converter

When calling the method from MainPage.xaml.cs it runs but the UI doesn't update

I'm creating an UWP app that is supposed to get some data from an API and display it, that happens on the "Refresh()" method.
Everything works fine until I try to do a search, that is the "Search()" method.
The "Search()" method is called from the MainPage.
public sealed partial class MarvelMenu : Page, INotifyPropertyChanged
{
//Backing field.
private ObservableCollection<Character> _marvelCharacters = new ObservableCollection<Character>();
//Property
public ObservableCollection<Character> MarvelCharacters
{
get { return _marvelCharacters; }
set
{
if (value != _marvelCharacters)
{
_marvelCharacters = value;
//Notify of the change.
NotifyPropertyChanged();
}
}
}
//PropertyChanged event.
public event PropertyChangedEventHandler PropertyChanged;
//PropertyChanged event triggering method.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private ObservableCollection<ComicBook> MarvelComics;
public MarvelMenu()
{
this.InitializeComponent();
MarvelComics = new ObservableCollection<ComicBook>();
}
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
var storageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///VoiceCommandDictionary.xml"));
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(storageFile);
Refresh();
}
public async void Refresh()
{
MyProgressRing.Visibility = Visibility.Visible;
MyProgressRing.IsActive = true;
ErrorTextBlock.Text = "";
MarvelCharacters.Clear();
while (MarvelCharacters.Count < 20)
{
Task t = MarvelFacade.PopulateMarvelCharactersAsync(MarvelCharacters);
await t;
}
try
{
this.MasterListBox.SelectedIndex = 0;
}
catch (Exception)
{
return;
}
MyProgressRing.IsActive = false;
MyProgressRing.Visibility = Visibility.Collapsed;
ErrorTextBlock.Text = MarvelFacade.errorMessage;
var attribute = await MarvelFacade.GetCharacterDataWrapperAsync();
var myAttribute = attribute.attributionText;
try
{
AttributeTextBlock.Text = myAttribute;
}
catch (Exception)
{
return;
}
}
public async void Search(string searchedCharacter)
{
MyProgressRing.Visibility = Visibility.Visible;
MyProgressRing.IsActive = true;
ErrorTextBlock.Text = "";
MarvelCharacters.Clear();
Task t = MarvelFacade.PopulateMarvelCharactersByNameAsync(searchedCharacter, MarvelCharacters);
await t;
MyProgressRing.IsActive = false;
MyProgressRing.Visibility = Visibility.Collapsed;
ErrorTextBlock.Text = MarvelFacade.errorMessage;
}
While running the app in debug mode I found that the C# code runs perfectly and actually retrieves the searched data from the API, however it is not displayed.
Even though I see Visual Studio go through each step in that method none of it is actually displayed.
<ListBox Name="MasterListBox"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ItemsSource="{x:Bind MarvelCharacters}"
Grid.RowSpan="3"
IsHitTestVisible="True"
SelectionChanged="MasterListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="data:Character">
<ListBoxItem Style="{StaticResource ListBoxItemStyle}"
Margin="-12,-11,-12,-13"
IsHitTestVisible="False">
<StackPanel Orientation="Horizontal">
<Ellipse Width="40"
Height="40"
Margin="4">
<Ellipse.Fill>
<ImageBrush ImageSource="{x:Bind thumbnail.small}"/>
</Ellipse.Fill>
</Ellipse>
<TextBlock Text="{x:Bind name}"
VerticalAlignment="Center"
Width="180"
TextTrimming="WordEllipsis"
FontSize="15"
Margin="10,0,0,0"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My ListBox is binding to "MarvelCharacters" that is an public ObservableCollection properties. So it displays fine when the app is launched but it does not refresh to show the search results.
Any one can help me I would really appreciate it.
The binding in Xaml has a property known as 'Mode'. This property defines the way it is binded.
There a 3 Modes : OneTime,OneWay,TwoWay
OneTime : using will let u bind ur data to ur UI only once and that is during intialization. after that the UI is on its own. x:Bind has default mode setup to OneTime While classic binding ( {Binding} ) has default to setup to OneWay
OneWay : Using this will ensure Your UI Updates every time the Data Updates.
{x:Bind name,Mode=OneWay}
{Binding name,Mode=OneWay}
while Classic binding doesn't require the explicit declaration you can simply bind but for Compiled Binding Explicit declaration is Necessary.

WPF Update grid visibility

After some search on web I have set up this simple example:
PropertyChangedBase.cs
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
//Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter:
Application.Current.Dispatcher.BeginInvoke((Action)(() => {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}
UserViewModel.cs
public class UserViewModel : PropertyChangedBase
{
private Visibility _showUserWindow = Visibility.Collapsed;
public Visibility ShowUserWindow {
get { return _showUserWindow; }
set {
_showUserWindow = value;
OnPropertyChanged("ShowUserWindow"); //This is important!!!
}
}
}
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid Margin="43,28,247,129" Background="AliceBlue" Visibility="{Binding ShowUserWindow}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="349,150,0,0" VerticalAlignment="Top" Width="75" PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
UserViewModel userViewModel;
public MainWindow() {
InitializeComponent();
userViewModel = new UserViewModel();
DataContext = userViewModel;
}
private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
userViewModel.ShowUserWindow = Visibility.Visible;
Thread.Sleep(1000);
userViewModel.ShowUserWindow = Visibility.Collapsed;
}
}
Right now grid becomes collapsed after 1 sec, I would like to update UI before timer starts. What I am doing wrong?
Edit:
Thread.Sleep line immitates some work, that takes some time to complete.
Grid should become visible before work starts and show some info about that work and become collapsed after work is done.
Well, you should consider doing the Thread.Sleep(1000) operation on a separate thread, not on the UI thread. Check this for that.
Other than that, try to use yourGrid.UpdateLayout() method after setting its visibility to collapsed.
LE:
Most probably, his Thread.Sleep(1000) stands for something like a database operation, for example, something that takes time.
LE2: A BackgroundWorker will do the trick. Check this link!
If you are using .NET 4.5, you can use Task.Delay() to allow UI to update itself before starting the actual work:
private async void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
userViewModel.ShowUserWindow = Visibility.Visible;
await Task.Delay(1);
Thread.Sleep(1000);
userViewModel.ShowUserWindow = Visibility.Collapsed;
}
Please note that await Task.Delay(1); should be used even after you replace Thread.Sleep(1000) with actual work.
However this should be used only if your work can be done only in UI thread and cannot be moved to a background thread (for example, massive loading of UI elements). Otherwise the correct approach is to move the work to a background thread:
private async void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
userViewModel.ShowUserWindow = Visibility.Visible;
await Task.Start(() => {
Thread.Sleep(1000);
};
userViewModel.ShowUserWindow = Visibility.Collapsed;
}

Designing responsive UI using WPF databinding

I am new to WPF. I am trying to understand MVVM pattern using WPF binding. I have below 2 classes
MainWindow.xamal
ViewModel
I have three controls
textbox which displays 'Name' property of ViewModel
textbox which displays 'Status' dependency property of ViewModel
Button which invokes'Execute' method of 'ViewModel' class.
Now, Execute() method is bit bulky so I have created a delegate and invoke it asynchronously. But still my UI is blocking and its not updating values of 'Status' dependency property
Refer to below classes.
App.xaml.cs
namespace bindingDemo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow mw = new MainWindow();
ViewModel vm = new ViewModel();
///Set data context property of main windows.
mw.DataContext = vm;
mw.Show();
}
}
}
MainWindow.xaml
<Window x:Class="bindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Text="{Binding Name, Mode=TwoWay}" Height="23" HorizontalAlignment="Left" Margin="76,26,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
<Button Command="{Binding Path=MyCommand}" Content="Button" Height="23" HorizontalAlignment="Left" Margin="76,127,0,0" Name="button1" VerticalAlignment="Top" Width="120" />
<TextBox Text="{Binding Path=Status}" Height="23" HorizontalAlignment="Left" Margin="76,55,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" />
</Grid>
ViewModel.cs
namespace bindingDemo
{
public class ViewModel : DependencyObject , ICommand
{
public string Status
{
get { return (string)GetValue(StatusProperty); }
set { SetValue(StatusProperty, value); }
}
// Using a DependencyProperty as the backing store for Status. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StatusProperty =
DependencyProperty.Register("Status", typeof(string), typeof(ViewModel), new UIPropertyMetadata("In Progress..."));
private ICommand _command = null;
public ViewModel()
{
Name = "Default Name";
}
public void Execute(object parameter)
{
Action a = new Action(() =>
{
///While this code is being executed, UI gets blocked.
Console.WriteLine(Name);
Name = "OK";
Status = "Connecting to database....";
Thread.Sleep(2000);
Status = "Connected to database....";
Thread.Sleep(2000);
Status = "Performing validations....";
Thread.Sleep(2000);
Status = "Data saved.";
});
/// Even if I have invoked operation asynchronously, UI is not getting updated
/// UI is freezing for 6 seconds and can directly see last 'Status' message on UI
Dispatcher.BeginInvoke(a, null);
}
public string Name { get; set; }
public ICommand MyCommand
{
get
{
return this;
}
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
}
Can somebody help me on this?
Regards,
Hemant
A ViewModel normally doesn't contain dependency properties. To be able to update the UI via data binding, it has to implement the INotifyPropertyChanged interface.
Try implementing your ViewModel like this:
public class ViewModel : INotifyPropertyChanged
{
private string _status;
public string Status
{
get { return _status; }
set
{
if(_status == value)
return;
_status = value;
OnPropertyChanged("Status");
}
}
public event EventHandler<PropertyChangedEventArgs> PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if(handler != null)
handler(new PropertyChangedEventArgs(propertyName));
}
// ...
}
Implementing ICommand on your view model seems rather odd, too.
Few things here:
DependencyProperty is for... well, classes with dependency properties. For view models, implement INotifyPropertyChanged. DependencyObject ties your inheritance now and it's not its intended usage.
You're invoking an action on a Dispatcher, and Dispatcher should be used to run a function on a dispatcher-thread, which in this case will be the UI thread. No wonder it gets blocked, you're calling a method on a UI thread. Dispatcher is useful if you want to change the UI-bound values (e.g. report some kind of progress) from the background task. You have to separate your logic, do the processing in background, then report the result.
So, that being said, your Execute should probably look like this (using C# 5):
private async Task DoStuff()
{
await Task.Delay(5000);
//or drop the async modifier and 'return Task.Delay(5000);'
}
public async void Execute(object parameter)
{
await DoStuff();
//Add some checks if it really was 'OK', catch exceptions etc
Name = "OK";
}
With C# 4 (untested):
private Task DoStuff()
{
return Task.Factory.StartNew(() => Thread.Sleep(5000));
}
public void Execute(object parameter)
{
DoStuff().ContinueWith(result => Name = "OK", TaskScheduler.FromCurrentSynchronizationContext());
//Same as above, probably should specify appropriate TaskOptions to run the continuation
//only when the task was completed successfully.
}

Wpf Label not being updated when using Thread.Sleep()

I have a Label the Content of which I would like to update after each second,after 3 seconds I only see the last string "Step 3..." What am I doing wrong and is there another way to achieve this if for some reason I cannot use Thread.Sleep():
View:
<Window x:Class="WpfApplication1.ScrollerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Scroller" DataContext="{StaticResource scrollerVM}" Height="150" Width="300">
<Grid>
<ListBox ItemsSource="{Binding Messages}" Width="200" Height="50" BorderThickness="0" VerticalAlignment="Top" HorizontalAlignment="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Text}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
<Button Width="70" Height="24" Content="Add new" Command="{Binding AddNew}" HorizontalAlignment="Left" Margin="0,56,0,30" />
</Grid>
</Window>
View model:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;
namespace WpfApplication1.Scroller
{
public class Message
{
public Message(string _text)
{
text = _text;
}
private string text;
public string Text
{
get { return text; }
set {text = value;}
}
}
public class ScrollerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public DelegateCommand AddNew { get; protected set; }
ObservableCollection<Message> _messages = new ObservableCollection<Message>();
public ObservableCollection<Message> Messages
{
get { return _messages; }
set
{
_messages = value;
OnPropertyChanged("Messages");
}
}
public ScrollerViewModel()
{
AddNew = new DelegateCommand(Add);
}
private void Add(object parameter)
{
UpdateProgress("Step 1...");
UpdateProgress("Step 2...");
UpdateProgress("Step 3...");
}
private void UpdateProgress(string step)
{
Messages.Clear();
Messages.Add(new Message(step));
Thread.Sleep(1000);
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
It is because you are sleeping in the UI thread. The UI won't have a chance to update until Add is finished. You can use a BackgroundWorker with ReportProgress to achieve what you want. Something like this:
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
worker.ReportProgress(1, "Step1");
Thread.Sleep(1000);
worker.ReportProgress(2, "Step2");
Thread.Sleep(1000);
worker.ReportProgress(3, "Step3");
};
worker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args)
{
string step = (string)args.UserState;
Messages.Clear();
Messages.Add(new Message(step));
};
worker.RunWorkerAsync();
The UI thread won't be occupied while DoWork is executed, but the code in ProgressChanged will be performed on the UI thread.
You should NEVER call Thread.Sleep when you´re on the UI Thread. This will block the Dispatcher and no rendering. binding updates etc. will occur during this time.
If you want to wait between your UpdateProgress() calls, use a DispatcherTimer.
You are clearing the messages before adding the new one.
private void UpdateProgress(string step)
{
Messages.Clear(); // <- Why?
Messages.Add(new Message(step));
Thread.Sleep(1000);
}

Categories

Resources