I'm trying to bind a string property to show in my status bar if my database is connected. Here's the code:
C#
public class TimeBase : INotifyPropertyChanged
{
private DXTickDB db;
string[] args = new string[] { };
public event PropertyChangedEventHandler PropertyChanged;
private bool isTBconnected;
public string connectionStatus { get; set; }
public bool tb_isconnected
{
get { return isTBconnected; }
set
{
if (value != isTBconnected)
{
isTBconnected = value;
if(isTBconnected == false)
{
connectionStatus = "TimeBase is not connected";
}
else
{
connectionStatus = "TimeBase is connected";
}
OnPropertyChanged("connectionStatus");
}
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#region TimeBase Connection
public void ConnectToTimeBase()
{
if (args.Length == 0)
args = new string[] { "not available for security reasons" };
db = TickDBFactory.createFromUrl(args[0]);
try
{
db.open(true);
tb_isconnected = true;
}
catch
{
tb_isconnected = false;
}
}
#endregion
This is the Xaml for the status bar in my main window:
<StatusBar Height="23" DockPanel.Dock="Bottom" Background="Green">
<StatusBarItem>
<StackPanel Orientation="Horizontal">
<TextBlock
Foreground="{StaticResource Foreground}"
Text="{Binding Path=connectionStatus}">
</TextBlock>
</StackPanel>
</StatusBarItem>
</StatusBar>
I'm trying to bind it to the string property connectionStatus but no text appears even though when I debug it I can see connectionStatus updated. Any suggestions to what's wrong here?
DataContext property should contain your model like so:
TimeBase timeBaseInstance;
public MainWindow()
{
timeBaseInstance = new TimeBase();
//Set the dataContext so bindings can iteract with your data
DataContext = timeBaseInstance;
InitializeComponent();
}
Related
I made an application with Windows template studio, As MVVM,
The Problem exists in ShellPage which contains some Controls, 2 Image , TextBlock, the NavigationView, and of course the Frame that holds all other pages.
The code here is for the TextBlock, but the Problem same for the 2 Image controls also.
in ShellPage.xaml:
xmlns:myControls="using:Numbers_to_Text.MyControls"
d:DataContext="{d:DesignInstance Type=viewmodels:ShellViewModel}"
Height="650" Width="1000" MaxHeight="650" MaxWidth="1000" MinHeight="650" MinWidth="1000"
mc:Ignorable="d" Background="{x:Null}">
<Page.Resources>
<helpers:AppSettings x:Key="AppSettings" />
</Page.Resources>
<TextBlock x:FieldModifier="public" x:Name="PageTitle" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="1"
Canvas.ZIndex="2" TextAlignment="DetectFromContent" HorizontalTextAlignment="DetectFromContent"
VerticalAlignment="Bottom" FontWeight="Bold" Text="{Binding ChangeTitle, Mode=TwoWay}"/>
in ShellPage.xaml.cs:
public ShellPage()
{
InitializeComponent();
DataContext = ViewModel;
ViewModel.Initialize(shellFrame, navigationView, KeyboardAccelerators);
}
and in ShellViewModel.cs
private void OnItemInvoked(WinUI.NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
NavigationService.Navigate(typeof(SettingsPage), null, args.RecommendedNavigationTransitionInfo);
ChangeTitle = "Settings";
}
else
{
var selectedItem = args.InvokedItemContainer as WinUI.NavigationViewItem;
var pageType = selectedItem?.GetValue(NavHelper.NavigateToProperty) as Type;
if (pageType != null)
{
NavigationService.Navigate(pageType, null, args.RecommendedNavigationTransitionInfo);
ChangeTitle= pageType.Name;
}
}
}
private string _changeTitle;
public string ChangeTitle
{
get { return _changeTitle= GetTitle(); }
set
{
_changeTitle = value;
RaisePropertyChanged(nameof(ChangeTitle));
}
}
private static string GetTitle()
{
try
{
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
return NavigationService.Frame.Content != null
? resourceLoader.GetString(NavigationService.Frame.Content.GetType().Name)
: "Error Page Title";
}
catch
{
return "Welcome to Main Page";
}
}
public event PropertyChangedEventHandler propertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propName = "")
{
propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
this.propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Why ChangeTitle not changing when navigation occured?
I used breakPoints to trace the ChangeTitle, I implemented PropertyChangedEventHandler inside the shellViewModel instead to make sure that the property setter is call the NotifyPropertyChanged, with no luck.
I am trying to move from WinForms to WPF, and am stuck on binding.
I have a label:
<Label Name="labelState" Content="{Binding state}" HorizontalAlignment="Right" Margin="10,10,10,10" FontSize="12" />
In the cs of the same userControl (named FormInput), I have :
public string state { get; set; }
public FormInput()
{
state = "ok";
InitializeComponent();
}
Why doesn't this work?
Thank you.
When you are binding something in WPF you need to use INotifyPropertyChanged
Implement a class follows,
class TestObject : INotifyPropertyChanged
{
private string _state;
public string State
{
get
{
return _state;
}
set
{
if (_state == value) return;
_state = value;
OnPropertyChanged("State");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
and in your FormInput
public FormInput()
{
InitializeComponent();
TestObject t = new TestObject();
labelState.DataContext = t;
t.State = "ok";
}
and XAML as follows,
<Label Name="labelState" Content="{Binding State}" HorizontalAlignment="Right" >
this my xml code
<TextBlock Grid.Column="0" Tag="{Binding id,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Text="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
this is my model
public string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; RaisePropertyChanged("Name"); }
}
when i set value to these two propertie ie. to id and Name
but its not notifying to Name ...
Simple Databinding Example with Updates. You can use this as a reference to get you started :)
public partial class MainWindow : Window, INotifyPropertyChanged
{
// implement the INotify
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _mytext;
public String MyText
{
get { return _mytext; }
set { _mytext = value; NotifyPropertyChanged("MyText"); }
}
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this; // set the datacontext to itself :)
MyText = "Change Me";
}
}
<TextBlock Text="{Binding MyText}" Foreground="White" Background="Black"></TextBlock>
I have a simple object (which is globally initiated in App.xaml.cs):
public class now_playing : INotifyPropertyChanged
{
// notify
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string p)
{
Debug.WriteLine(p + ": notify propertychanged");
PropertyChangedEventHandler handler = PropertyChanged;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
// artist
public string artist
{
get
{
return _artist;
}
set
{
_artist = value;
NotifyPropertyChanged("artist");
}
}
private string _artist;
// album
public string album
{
get
{
return _album;
}
set
{
_album = value;
NotifyPropertyChanged("album");
}
}
private string _album;
// track title
public string tracktitle
{
get
{
return _tracktitle;
}
set
{
_tracktitle = value;
NotifyPropertyChanged("tracktitle");
}
}
private string _tracktitle;
}
Whenever I change the values, the class does notify (I see the debug).
So I guess the problems lies in my XAML or the code behind.
Page code:
public sealed partial class nowplaying : Page
{
// artistdata
public string artist { get { return App.nowplaying.artist; } }
// albumdata
public string album { get { return App.nowplaying.album; } }
// trackdata
public string tracktitle { get { return App.nowplaying.tracktitle; } }
public nowplaying()
{
this.InitializeComponent();
this.DataContext = this;
}
}
XAML:
<Grid Margin="50">
<TextBlock Text="{Binding tracktitle}" Foreground="White" FontSize="40"/>
<TextBlock Foreground="#dcdcdc" FontSize="20" Margin="0,50,0,0">
<Run Text="{Binding artist}"/>
<Run Text=" - "/>
<Run Text="{Binding album}"/>
</TextBlock>
</Grid>
Why does the UI not update when I change values?
Stack trace:
Music.exe!Music.App.InitializeComponent.AnonymousMethod__6(object sender = {Music.App}, Windows.UI.Xaml.UnhandledExceptionEventArgs e = {Windows.UI.Xaml.UnhandledExceptionEventArgs}) Line 50 C#
Music.exe!play_music.MessageReceivedFromBackground(object sender = null, Windows.Media.Playback.MediaPlayerDataReceivedEventArgs e = {Windows.Media.Playback.MediaPlayerDataReceivedEventArgs}) Line 57 C#
UPDATE: problem solved! I had to use a dispatcher when calling the propertychanged event:
CoreDispatcher dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
if (PropertyChanged != null)
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
this.PropertyChanged(this, new PropertyChangedEventArgs(p));
});
}
You "loose" the change notification in the properties in the Page as these properties do not have any change notifiaction.
Try using now_playing directly:
public sealed partial class nowplaying : Page
{
public now_playing NowPlaying { get { return App.nowplaying; } }
public nowplaying()
{
this.InitializeComponent();
this.DataContext = this;
}
}
and
<Run Text="{Binding NowPlaying.artist}"/>
Otherwise you need to implement INotifiyPropertyChanged in nowplaying and forward the events from now_playing.
You actually binding to artist, album and tracktitle Of nowplaying class which does implement INotifyPropertyChanged
How do I get the text bound to txtMessage from the second view model? When I had only one view model, the text was working fine. It does not work anymore when I moved the actual download code to second view model. Am I missing something? Any help appreciated.
Xaml:
<DockPanel DockPanel.Dock="Top">
<TextBlock x:Name="txtMessage" DockPanel.Dock="Top" Margin="5" Text="{Binding viewModel1.Message}" />
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5,5">
<ProgressBar Width="300" Visibility="{Binding IsDownloading, Converter={converter:VisibilityConverter}}" IsIndeterminate="True" />
<Button Content="Cancel" />
</StackPanel>
</DockPanel>
<Button Content="Download" Width="120" Margin="0,0,5,0" Name="btnSubmit" Click="btnSubmit_Click" />
CodeBehind:
public partial class DownloadWindow: Window
{
DownloadWindowViewModel viewModel = new DownloadWindowViewModel();
public DownloadWindow()
{
InitializeComponent();
this.DataContext = viewModel;
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
viewModel.IsDownloading = true;
viewModel.Download();
}
}
viewModel:
public class DownloadWindowViewModel: INotifyPropertyChanged
{
Thread downloadThread;
public DownloadViewModel viewModel1;
public DownloadWindowViewModel()
{
viewModel1 = new DownloadViewModel();
}
private bool _isDownloading; = false;
public bool IsDownloading
{
get
{
return _isDownloading;
}
set
{
_isDownloading; = value;
OnPropertyChanged("IsDownloading");
}
}
public void Download()
{
viewModel1.Download();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
viewModel1:
public class DownloadViewModel: INotifyPropertyChanged
{
Thread _thread;
public void Download()
{
ThreadStart threadStart = delegate()
{
StartDownload();
};
_thread = new Thread(threadStart);
_thread.IsBackground = true;
_thread.Start();
}
private void StartDownload()
{
for (int i = 10; i < 1500; i++)
{
Thread.Sleep(5000);
Message = "Downloading " + i.ToString();
}
}
private string _message = "";
public string Message
{
get
{
return _message;
}
set
{
if (_message != value)
{
_message = value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Your viewModel1 has to be a property, and it's a field at the moment. Change it to:
public DownloadViewModel viewModel1 { get; set; }
Explanation why such restriction exists, can be found here (primarily due to notification/verifications mechanisms simply not working for fields):
Why does WPF support binding to properties of an object, but not fields?