I have two Windows, my MainWindow and a CounterWindow; on the MainWindow is a button with a binding and on my CounterWindow is a label with the same binding
When data is retrieved, the Button does receive the new data, but the CounterWindow label doesn't update.
Note, it turns to 0 on initializing (so the Binding functions!(?))
public class CounterModel : INotifyPropertyChanged
{
public string CurrentCount {
get { return mCurrentCount; }
set
{
if (value == mCurrentCount) return;
mCurrentCount = value;
OnPropertyChanged();
}
}
public CounterModel() {
UpdateCounters();
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
string mCurrentCount;
public void UpdateCounters(int i = 0)
{
CurrentCount = i.ToString();
}
}
}
MainWindow codehbehind:
public MainWindow()
{
InitializeComponent();
CounterBtn.DataContext = cntMdl;
}
CounterModel cntMdl = new CounterModel();
CounterWindow CodeBehind:
public CounterWindow()
{
InitializeComponent();
DataContext = new CounterModel();
}
CounterWindow Xaml
<TextBlock Text="{Binding CurrentCount}" FontSize="900" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Consolas" Foreground="White" MouseDown="TextBlock_MouseDown"/>
what am I missing?
Do not create a new CounterModel instance for the CounterWindow. Remove
DataContext = new CounterModel();
from the CounterWindow constructor.
In case you are showing the CounterWindow from the Button's Click event handler, pass the current DataContext to the new window:
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var window = new CounterWindow { DataContext = button.DataContext };
window.Show();
}
Related
I have a NavigationView that loads a page into its associated frame.
On the loaded page there is a button, how can I use this button to change the properties of the NavigationView.
I know that to update the page in the frame is:
Frame.Navigate(typeof(Window2));
So I thought it might be:
Frame.NavigationView.IsEnabled = False
But this isn't valid.
Is there a way to do this?
You can not set NavigationView.IsEnabled directly from loaded page.You can set a property in a viewmodel to bind with the IsEnabled of navigationView.When you navigate a new page by Frame.Navigate(),pass the viewmodel to the new page.You can set the property to false when you click the button in the page.In this case,the navigationView will be disabled.
.xaml:
<NavigationView IsEnabled="{x:Bind MyViewModel.IsEnabled,Mode=OneWay}">
<NavigationView.MenuItems>
<NavigationViewItem Content="Item 1"></NavigationViewItem>
</NavigationView.MenuItems>
<Frame x:Name="ContentFrame"/>
</NavigationView>
.cs:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private bool isEnabled = false;
public bool IsEnabled {
get { return isEnabled; }
set {
isEnabled = value;
OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public sealed partial class MainPage: Page
{
public MainPage()
{
this.InitializeComponent();
MyViewModel = new ViewModel();
}
private ViewModel MyViewModel { get; set; }
}
When you want to navigate a new page,pass the viewmodel:
ContentFrame.Navigate(typeof(Page1),MyViewModel);
Page1.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
isEnabledVM = (e.Parameter as ViewModel);
}
private ViewModel isEnabledVM { get; set; }
private async void Button_Click(object sender, RoutedEventArgs e)
{
isEnabledVM.IsEnabled = false;
}
I would like to propagate DataContext upwards from a dynamically created DataTemplate hosted withing a ContentControl such as following one:
var myFactory = new FrameworkElementFactory(controlTypeToDisplay);//= typeof(MyControl)
var dctxBinding = new Binding
{
Path = new PropertyPath("DataContext.Dctx"),
Mode = BindingMode.OneWayToSource,
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ContentControl), 1)
};
myFactory.SetBinding(FrameworkElement.DataContextProperty, dctxBinding);
return new DataTemplate() { VisualTree = myFactory };
However the result of such binding is null even though the DataContext is being set within MyControl's constructor. DataContext of MyControl definitely is not set null further down the road, constructor is invoked before setter of Dctx. How can I fix the binding so that MyControl's DataContext and Dctx property are always in sync?
Full minimal example of the issue (should display two "FooBar" TextBlocks if working correctly):
//MyControl.xaml
<Grid>
<TextBlock Text="{Binding}"/>
</Grid>
//MyControl.xaml.cs
public MyControl()
{
InitializeComponent();
DataContext = "FooBar";
this.DataContextChanged += MyControl_DataContextChanged;
}
private void MyControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
MessageBox.Show("An unexpected change");
}
//MainWindow.xaml
<StackPanel>
<ContentControl ContentTemplate="{Binding DataTemplate}"/>
<TextBlock Text="{Binding Dctx, TargetNullValue='<null>'}" />
</StackPanel>
//MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
private Type controlTypeToDisplay = typeof(MyControl);
public DataTemplate DataTemplate
{ get {/*see first listing*/ } }
private object _dctx;
public object Dctx
{
get { return _dctx; }
set { _dctx = value; RaisePropertyChanged(); }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName]string caller = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
The DataContext of the root element in the ContentTemplate of a ContentControl is the ContentControl's Content. Bind the Content property to your DataContext:
<ContentControl Content="{Binding Dctx}" ContentTemplate="{Binding DataTemplate}"/>
You must also set the DataContext of the ContentControl itself (or the parent window) somewhere, and add some visual element to the DataTemplate:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public DataTemplate DataTemplate
{
get {
var myFactory = new FrameworkElementFactory(typeof(TextBlock));
myFactory.SetBinding(TextBlock.TextProperty, new Binding(".")); //bind to the DataContext
return new DataTemplate() { VisualTree = myFactory };
}
}
private object _dctx = new object(); //set the Dxtc property somewhere
public object Dctx
{
get { return _dctx; }
set { _dctx = value; RaisePropertyChanged(); }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName]string caller = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
I have a UserControl that I add to my main application.
That UserControl contains a button for a UIElement
The UserControl contains a DispatchTimer and every 2 seconds based on some int values determines what the button image will be.
One of the methods called in the UserControl should set it's image but the control never displays the image that it was changed to.
public void SetNormal()
{
btnFlashAlert.Content = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
}
Is there something i'm missing to get the look of the control update on the main application?
When I look at what .Content contains, it is correct. The UI doesn't reflect the change.
XAML
<UserControl x:Class="SC.FlashSystem.MainButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="53" Width="164">
<Button x:Name="btnFlashAlert" Background="{x:Null}" BorderBrush="{x:Null}" Cursor="Hand" Click="btnFlashAlert_Click">
<Button.Template>
<ControlTemplate>
<Image Source="Images/FlashButton.png"/>
</ControlTemplate>
</Button.Template>
</Button>
Codebehind Updated
public partial class MainButton : UserControl
{
private SupportConsoleWeb.MessageData messageCounts { get; set; }
private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
private BitmapImage NormalImage { get; set; }
private BitmapImage CriticalImage { get; set; }
private BitmapImage AlertImage { get; set; }
private BitmapImage InfoImage { get; set; }
public MainButton()
{
InitializeComponent();
messageCounts = new SupportConsoleWeb.MessageData();
messageCounts.CriticalCount = 0;
messageCounts.AlertCount = 0;
messageCounts.InfoCount = 0;
NormalImage = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
CriticalImage = new BitmapImage(new Uri("Images/FlashButtonRed.png", UriKind.RelativeOrAbsolute));
AlertImage = new BitmapImage(new Uri("Images/FlashButtonOrange.png", UriKind.RelativeOrAbsolute));
InfoImage = new BitmapImage(new Uri("Images/FlashButtonGreen.png", UriKind.RelativeOrAbsolute));
flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
flashButtonChangeTimer.Tick += flashButtonChangeTimer_Tick;
flashButtonChangeTimer.Start();
}
void flashButtonChangeTimer_Tick(object sender, EventArgs e)
{
btnFlashAlert.Dispatcher.BeginInvoke(new Action(() =>
{
if (btnFlashAlert.Content == null)
{
SetNormal();
}
else if (messageCounts.CriticalCount > 0 && btnFlashAlert.Content.Equals(CriticalImage))
{
SetNormal();
}
else if (messageCounts.AlertCount > 0 && btnFlashAlert.Content.Equals(AlertImage))
{
SetNormal();
}
else if (messageCounts.InfoCount > 0 && btnFlashAlert.Content.Equals(InfoImage))
{
SetNormal();
}
else if (messageCounts.CriticalCount > 0)
{
SetCritical();
}
else if (messageCounts.AlertCount > 0)
{
SetAlert();
}
else if (messageCounts.InfoCount > 0)
{
SetInfo();
}
}));
}
public void UpdateMessageCounts(SupportConsoleWeb.MessageData messageCounts)
{
this.messageCounts = messageCounts;
}
private void btnFlashAlert_Click(object sender, RoutedEventArgs e)
{
MainWindow window = new MainWindow();
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
window.ShowDialog();
}
public void SetMessageCount(int criticalCount, int alertCount, int infoCount)
{
messageCounts.CriticalCount = criticalCount;
messageCounts.AlertCount = alertCount;
messageCounts.InfoCount = infoCount;
}
private void SetNormal()
{
btnFlashAlert.Content = NormalImage;
}
private void SetCritical()
{
btnFlashAlert.Content = CriticalImage;
}
private void SetAlert()
{
btnFlashAlert.Content = AlertImage;
}
private void SetInfo()
{
btnFlashAlert.Content = InfoImage;
}
}
Change your XAML To this
<Image Source="{Binding TheImage}"/>
Add notify property changed
public partial class MainButton : UserControl, INotifyPropertyChanged
Create the OnPropertyChanged Event
void OnPropertyChanged(String prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
public event PropertyChangedEventHandler PropertyChanged;
Create a Bitmap prop and notify the prop changed event
private BitmapImage _TheImage;
public BitmapImage TheImage
{
get { return _TheImage; }
set { _TheImage = value; OnPropertyChanged("TheImage"); }
}
In your initializer
public MainButton()
{
this.DataContext = this;
InitializeComponent();
TheImage = new BitmapImage();
Now in your setting methods call
TheImage = //Your Bitmap Goes here
I know this seems excessive but you will see it is a much cleaner implementation in the long run.
I believe its an issue with picture selection logic not having a default image when none of the conditions are met...
With that said, IMHO the picture logic would be better expressed by having all images pre-loaded and their visibility initially set to hidden. Then bind the visibility of each image to a specific flag boolean on the VM. Which the timer event can simply turn on or off the boolean(s) which will ultimately show or hide images as needed.
That removes any latency due to loading and showing of images for they will be pre-loaded; also it will solve any possible future memory issues due to loading/unloading of images.
Example
The following example has a button with two images. Both image's visibility is bound to Booleans on the VM. The VM has one Boolean which the imageas work off of and a timer which changes its status every two seconds switching the images.
Xaml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Button x:Name="bStatus" Width="48" Height="48">
<StackPanel Orientation="Vertical">
<Image Source="Images\Copy-icon.png" Visibility="{Binding IsCopyOn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
<Image Source="Images\Recycle-icon.png"
Visibility="{Binding IsRecycleOn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</Button>
VM
public class MainVM : INotifyPropertyChanged
{
private bool _bSwitch;
private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
public bool IsRecycleOn
{
get { return _bSwitch; }
}
public bool IsCopyOn
{
get { return !_bSwitch; }
}
public MainVM()
{
flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
flashButtonChangeTimer.Tick += (sender, args) =>
{
_bSwitch = ! _bSwitch;
OnPropertyChanged("IsCopyOn");
OnPropertyChanged("IsRecycleOn");
};
flashButtonChangeTimer.Start();
}
/// <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>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I have a listbox in my WPF app. The definition is given below:
<ListBox Margin="17.493,33.32,22.491,26.656" Name="lstData"
PreviewMouseLeftButtonDown="ListBox_MouseDown"
IsTextSearchEnabled="False" />
In the code behind, I bind the ListBox to a List. When the value is selected from the List Box, in my code behind, I want to be able to retrieve that value. How do I do it? Sample C# code will be helpful.
Thanks.
You can just bind to an item in your code behind
Example:
<ListBox Margin="17.493,33.32,22.491,26.656" Name="lstData"
SelectionChanged="ListBox_selectionChanged"
IsTextSearchEnabled="False"
ItemsSource="{Binding MyItems}"
SelectedItem="{Binding MySelectedItem}"/>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private ObservableCollection<MyItemType> _myItems = new ObservableCollection<MyItemType>();
public ObservableCollection<MyItemType> MyItems
{
get { return _myItems; }
set { _myItems = value; }
}
private MyItemType _mySelectedItem;
public MyItemType MySelectedItem
{
get { return _mySelectedItem; }
set { _mySelectedItem = value; NotifyPropertyChanged("MySelectedItem"); }
}
private void ListBox_selectionChanged(object sender, SelectionChangedEventArgs e)
{
MessageBox.Show(_mySelectedItem);
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
UPDATE 1 : You can download the sample project from here.
Can you please help me to find the error in my code. I can't able to assign item source to combo box as well as button click event in WinRT app. I am using MVVM and MetroEventToCommand. I am new to MVVM concept, so please answer my silly question.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Click Here">
<mvvm:EventToCommandManager.Collection>
<mvvm:EventToCommand Command="{Binding ButtonClickCommand}" Event="Click"/>
</mvvm:EventToCommandManager.Collection>
</Button>
<ComboBox x:Name="FontsCombo" Height="50" Width="150" SelectedItem="{Binding SelectedFont}" ItemsSource="{Binding fonts}" />
<TextBlock FontSize="30" Text="{Binding SelectedFont}"/>
</Grid>
public MainPage()
{
this.InitializeComponent();
this.DataContext = new VM();
}
public class VM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public RelayCommand ButtonClickCommand { get; set; }
private ObservableCollection<string> _fonts = new ObservableCollection<string>();
public ObservableCollection<string> fonts
{
get { return _fonts; }
set
{
_fonts = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("fonts"));
}
}
}
private string _SelectedFont = "";
public string SelectedFont
{
get { return _SelectedFont; }
set
{
// Some logic here
_SelectedFont = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedFont"));
}
}
}
public VM()
{
fonts.Add("Arial");
fonts.Add("Courier New");
fonts.Add("Times New Roman");
ButtonClickCommand = new RelayCommand(Click);
}
private void Click()
{
new Action(async () => await new Windows.UI.Popups.MessageDialog("Testing dialog").ShowAsync()).Invoke();
}
}
For the SelectedItem, you didn't specify the Mode=TwoWay :
<ComboBox x:Name="FontsCombo" Height="50" Width="150" SelectedItem="{Binding SelectedFont, Mode=TwoWay}" ItemsSource="{Binding fonts}" />
EDIT
I found the solution :
public class VM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public RelayCommand ButtonClickCommand { get; set; }
private ObservableCollection<string> _fonts;
public ObservableCollection<string> fonts
{
get { return _fonts; }
set
{
_fonts = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("fonts"));
}
}
}
private string _SelectedFont;
public string SelectedFont
{
get { return _SelectedFont; }
set
{
// Some logic here
_SelectedFont = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedFont"));
}
}
}
public VM()
{
this.fonts = new ObservableCollection<string>();
fonts.Add("Arial");
fonts.Add("Courier New");
fonts.Add("Times New Roman");
ButtonClickCommand = new RelayCommand(Click);
}
private void Click()
{
new Action(async () => await new Windows.UI.Popups.MessageDialog("Testing dialog").ShowAsync()).Invoke();
}
}
If I instance fonts in the constructor, the UX is not freezing anymore.