How to make a TextBlock of an UWP-Application update automatically? - xaml

I have a background-thread which updates a Integer-value once per second. How can I map this Integer to a Text-Field of my XAML form that the Form always shows the current value and updates automatically if the Integer changes?

You can use a ViewModel with a binding.
With OnPropertyChanged() it will automatically changed and displayed in your UI.
here is an example to give you an idea
use in your xaml:
<TextBox x:Name="MyTextBox" Text="{Binding Name}".../>
in your code behind:
...
var vm = new ViewModel("Nr.7");
this.BindingContext = vm;
foreach(var x in Whatever)
{
vm.Name = x;
}
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MyAppNamespace
{
// This class implements INotifyPropertyChanged
// to support one-way and two-way bindings
// (such that the UI element updates when the source
// has been changed dynamically)
public class ViewModel : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
}
public ViewModel(string value)
{
this.name = value;
}
public string Name
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}

Related

Property change is not propagated to binding target (UWP)

I have a ViewModel and a Class. They look like this:
//viewmodel
public class MyViewModel : ViewModelBase {
private MyClass myClass;
public MyClass MyClass{
get{
return myClass;
}
set{
this.myClass = value;
base.OnPropertyChanged();
}
}
private string testString;
public string TestString{
get{
return testString;
}
set{
this.testString = value;
base.OnPropertyChanged();
}
}
public MyViewModel(){
this.MyClass = new MyClass();
this.TestString = "blah, blah, blah"
}}
//class
public class MyClass : ViewModelBase{
private string myString;
public string MyString{
get {
return myString
}
set{
this.myString = value;
base.OnPropertyChanged();
}
}
public MyClass (){
this.MyString = "25"; }}
The base class - ViewModelBase implements INotifyChange and contains OnPropertyChanged handler logic.
I have a UserControl where I wish to bind values from MyClass like this:
<TextBlock Text="{x:Bind Path=MyViewModel.MyClass.MyString, Mode=TwoWay}"></TextBlock>
However this does not work. Value is binded in initialisation correctly, but any change in MyViewModel.MyClass.MyString is not reflected in texblock, the text remains the same. The OnPropertyChange is raised, the breakpoint in ViewModelBase is hit with MyString value changed, but it is somehow not propagated to texblock.
Binding on simple value from MyClass works like charm, this textblock is updated, when property is changed:
<TextBlock Text="{x:Bind Path=MyViewModel.TestString, Mode=TwoWay}"></TextBlock>
What am I missing? Why the textblock with binding to "MyViewModel.MyClass.MyString" is not being updated?
I have tested the code you provided and don't think there is any problem with it. Where the problem could be, however, is your Page's code-behind. By any chance, aren't you using this as the property declaration?
public MyViewModel MyViewModel => new MyViewModel();
Because in this case, every access to MyViewModel property evaluates as a new instance of MyViewModel class. In this configuration, you would properly change the property, but the UI would never notice, as you would update the property on a new instance. If you instead use
public MyViewModel MyViewModel { get; } = new MyViewModel();
You will get the correct behavior of creating only one instance when the page is created.
I have the following code:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public MyViewModel MyViewModel { get; } = new MyViewModel();
private void Button_Click(object sender, RoutedEventArgs e)
{
MyViewModel.MyClass.MyString = "test";
}
}
And my simple ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

RadioButton not showing as "Checked" when bound value is true

In my project I am setting a bool property as true when constructing my ViewModel.
In the View, I have a RadioButton which is bound to the value of this property. The first time I open the View, the RadioButton is "checked" (perfect, exactly what I want!).
However, if I close and then re-open the View, the RadioButton is not "checked", despite the bound property having a value of 'true'. In my constructor I am setting 'StaggeredMode' to true, but the setter is being called three times (firstly, value = true; secondly, value = false; thirdly, value = true)
Any help will be appreciated!
XAML:
<RadioButton GroupName="AppointmentStart"
Content="Staggered"
IsChecked="{Binding StaggeredMode, Mode=TwoWay}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="310,247,0,0" />
C#:
private bool staggeredMode;
public bool StaggeredMode
{
get { return staggeredMode; }
set { staggeredMode = value; }
}
Your StaggeredMode property should be observable so the view can be notified about property changes from your ViewModel.
You can implement INotifyPropertyChanged by yourself or use one of existing implementations like this, this or this.
Here is a basic implementation of INotifyPropertyChanged interface:
public abstract class ObservableObject : INotifyPropertyChanged
{
public virtual void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
Then you derive your ViewModel from ObservableObject and raise OnPropertyChanged event every time your property value has been changed:
public class MyViewModel : ObservableObject
{
...
private bool staggeredMode;
public bool StaggeredMode
{
get { return staggeredMode; }
set
{
staggeredMode = value;
OnPropertyChanged("StaggeredMode");
}
}
...
}

Populate DataGrid through Button Command

TheContext refers to my ViewModel in the resources section
<DataGrid DataContext="{StaticResource TheContext}"
ItemsSource="{Binding Path=Cars}">
This is my viewModel.cs
public CarsSearchResultsViewModel()
{
ButtonCommand = new DelegateCommand(x => GetCars());
}
public void GetCars()
{
List<Car> cars = new List<Car>();
cars.Add(new Car() { Make = "Chevy", Model = "Silverado" });
cars.Add(new Car() { Make = "Honda", Model = "Accord" });
cars.Add(new Car() { Make = "Mitsubishi", Model = "Galant" });
Cars = new ObservableCollection<Car>(cars);
}
private ObservableCollection<Car> _cars;
public ObservableCollection<Car> Cars
{
get { return _cars; }
private set
{
if (_cars == value) return;
_cars = value;
}
}
I have tried adding OnPropertyChanged("Cars"), I have tried adding adding People = null before adding the list, I have tried adding UpdateSourceTrigger=PropertyChanged to the ItemsSource, and before using ObservableCollection I tried using IViewCollection.
Im not trying to update or delete from a collection, just populate the grid with a button click. If i run GetCars() in the constructor without the command it works fine.
Your ViewModel needs to implement the INotifyPropertyChanges interface, and call OnpropertyChanged in the setter of your ObservableCollection so that when you reinstated the UI will get notified so you Cars property should looks somethink like this :
private ObservableCollection<Car> _cars ;
public ObservableCollection<Car> Cars
{
get
{
return _cars;
}
set
{
if (_cars == value)
{
return;
}
_cars = value;
OnPropertyChanged();
}
}
The Cars collection needs to be defined inside TheContext class since it is your Context and that last one needs to implement the mentioned interface :
public class TheContext:INotifyPropertyChanged
{
//Cars property and your code ..
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}

Windows 8 Combobox PlaceholderText

i am using "visual studio express for windows 8" and using Combo Box control as
<ComboBox Name="Categories" >
<x:String>apple</x:String>
<x:String>ball</x:String>
<x:String>cat</x:String>
<x:String>dog</x:String>
</ComboBox>
i want to show placeholder text in it to show some text until user hasn't selected any item from it. But when i use property PlaceholderText as described in microsoft reference to show text but when i use it the sdk shows this error
The member "PlaceholderText" is not recognized or is not accessible.
or is there any other method so that i can show some default text in Combobox.
Thanks.
This is for Windows 8.1 preview and not Windows 8 development. You will need to install the preview at this time before you can use and develop with this combobox. Looking at the docs for placeholder it states:
Minimum supported client Windows 8.1 Preview
Edit
To do this manually simply preload the combobox by hand. Here is an example, let us start with the ViewModel where the constructor will load an initial value into the combobox named "Loading"
public class MainVM : INotifyPropertyChanged
{
private List<string> _dataList;
public List<string> ComboData
{
get { return _dataList; }
set
{
if (_dataList != value)
{
_dataList = value;
OnPropertyChanged();
}
}
}
public MainVM()
{
ComboData = new List<string> {"Loading..."};
}
#region INotify Property Changed Implementation
/// <summary>
/// Event raised when a property changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the property that has changed.</param>
public void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Now on the main page xaml bind to ComboData, but we need to be wary of the first situation where a list of one items will have loading, and we want to make that the selected item.
<ComboBox ItemsSource="{Binding ComboData}" Height="30" Width="300" Loaded="OnLoaded" />
Ok, in the code behind of the page we will set our datacontext to be the ViewModel we setup before, but also have an OnLoaded method which checks for the 1 item loading situation. In the below example we simulate a 3 second delay of loading the rest of the data.
public sealed partial class MainPage : Page
{
public MainVM ViewModel { get; set; }
public MainPage()
{
this.InitializeComponent();
DataContext = ViewModel = new MainVM();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var cBox = sender as ComboBox;
if (cBox != null)
{
if ((cBox.Items != null) && (cBox.Items.Count == 1))
{
cBox.SelectedIndex = 0;
// Debug code to simulate a change
Task.Run(() =>
{
// Sleep 3 seconds
new System.Threading.ManualResetEvent(false).WaitOne(3000);
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{ ViewModel.ComboData = new List<string> {"Alpha", "Gamma", "Omega"}; });
});
}
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}

Why my WP emulator stops on splashscreen

I created a WP Class Library BusinessLogic project which is composed by these three class
1) Bottle Class
namespace BusinessLogic
{
public class Bottle : INotifyPropertyChanged
{
// Due to INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
// Proprietà Title
private string title;
public string Title
{
set
{
if (title != value)
{
title = value;
OnPropertyChanged("Title");
}
}
get
{
return title;
}
}
// Proprietà PhotoFileName
private string photoFileName;
public string PhotoFileName
{
set
{
if (photoFileName != value)
{
photoFileName = value;
OnPropertyChanged("PhotoFileName");
}
}
get
{
return photoFileName;
}
}
protected virtual void OnPropertyChanged(string propChanged)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propChanged));
}
}
}
2) Bottles Class
namespace BusinessLogic
{
public class Bottles : INotifyPropertyChanged
{
// Due to INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
// Proprietà MainTitle
private string mainTitle;
public string MainTitle
{
set
{
if (mainTitle != value)
{
mainTitle = value;
OnPropertyChanged("MainTitle");
}
}
get
{
return mainTitle;
}
}
// Proprietà bottles
private ObservableCollection<Bottle> bottleSet = new ObservableCollection<Bottle>();
public ObservableCollection<Bottle> BottleSet
{
set
{
if (bottleSet != value)
{
bottleSet = value;
OnPropertyChanged("BottleSet");
}
}
get { return bottleSet; }
}
protected virtual void OnPropertyChanged(string propChanged)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propChanged));
}
}
}
3) BottlesPresenter Class
namespace BusinessLogic
{
public class BottlesPresenter : INotifyPropertyChanged
{
// Due to INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
// Proprietà BottleMatrix
private Bottles bottlesMatrix;
public Bottles BottlesMatrix
{
protected set
{
if (bottlesMatrix != value)
{
bottlesMatrix = value;
OnPropertyChanged("BottlesMatrix");
}
}
get { return bottlesMatrix; }
}
public BottlesPresenter()
{
XmlSerializer xml = new XmlSerializer(typeof(Bottles));
using (StreamReader fileReader = new StreamReader(#"C:\Stuff\WindowsPhone\AppLearningHowTo2\AppLearningHowTo2\DAL\DB.xml"))
{
BottlesMatrix = (Bottles)xml.Deserialize(fileReader);
}
}
protected virtual void OnPropertyChanged(string propChanged)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propChanged));
}
}
}
The BottlePresenter constructor should deserialize from an xml file located into the file system. It contains the following tags
<?xml version="1.0"?>
<Bottles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MainTitle>MainTitle</MainTitle>
<Bottleset>
<Bottle>
<Title>Title1</Title>
<PhotoFileName>PhotoFileName1</PhotoFileName>
</Bottle>
<Bottle>
<Title>Title2</Title>
<PhotoFileName>PhotoFileName2</PhotoFileName>
</Bottle>
</Bottleset>
</Bottles>
Then I created a WP Application and I made a reference to the BusinessLogic.dll project library.
In the MainPage.xaml file I put the XML namespace declaration
xmlns:businesslogic="clr-namespace:BusinessLogic;assembly=BusinessLogic"
I then instantiated the BottlesPresenter class in the MainPage.xaml Resources collection
<phone:PhoneApplicationPage.Resources>
<businesslogic:BottlesPresenter x:Key="bottlesPresenter" />
</phone:PhoneApplicationPage.Resources>
And finally put a TextBlock in the content area with a binding to that resource:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Source={StaticResource bottlesPresenter},
Path=Bottles.MainTitle}" />
Unfortunately I launch the debugger, the emulator switch on, reach the splashscreen and doesn't go on.
In a nutshell: I can't reach to create an instance of the BottlesPresenter class.
I banged my head against the wall for weeks without finding a solution.
Please could someone give me a hand?
Thank you very much
Antonio
Emulator behaves like that when WP7 cannot construct Application object. From question, I see only 1 reference from Application to your code. It's BottlePresenter in Resources.
XamlLoader tries to create instance of this type.
See what's inside your BottlePresenter constructur:
public BottlesPresenter()
{
XmlSerializer xml = new XmlSerializer(typeof(Bottles));
using (StreamReader fileReader = new StreamReader(
#"C:\Stuff\WindowsPhone\AppLearningHowTo2\AppLearningHowTo2\DAL\DB.xml"))
{
BottlesMatrix = (Bottles)xml.Deserialize(fileReader);
}
}
First line is OK.
Second line is OK.
Third line causes exception, because path "C:\Stuff\WindowsPhone\AppLearningHowTo2\AppLearningHowTo2\DAL\DB.xml" is not acceptable on Windows Phone.
All files you can access is Content of your XAP, Resources in your assembly, and files in Isolated Storage.
Following articles might help you All about WP7 Isolated Storage - Read and Save Text files