I am new to WPF. My code is as follows:
In my MainWindow.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Label HorizontalAlignment="Left" Margin="63,30,0,0" Grid.Row="0" VerticalAlignment="Top" Content="{Binding myVal}" Height="39" Width="71"/>
<Button Grid.Row="1" x:Name="btnSelect" Content="Select" Click="btnSelect_Click_1" Margin="396,0,10,0"/>
</Grid>
and MainWindow.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
private bool _myboolVal;
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void btnSelect_Click_1(object sender, RoutedEventArgs e)
{
if (myVal== false)
{
myVal = true;
}
else
{
myVal= true;
}
}
public bool myVal
{
get { return _myboolVal; }
set { _myboolVal= value; OnPropertyChanged("myVal"); }
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
But the value of the label is always false.
Your logic in btnSelect_Click_1 is incorrect. Update it to:
private void btnSelect_Click_1(object sender, RoutedEventArgs e)
{
myVal = !myVal;
}
take a look at this sample (MVVM, Command binding, MVVMLight)
Please set the Mode to TwoWay ,then it will works.
Content="{Binding myVal,Mode=TwoWay}"
Related
Im trying to change 2 textblocks with data binding. The propertyChanged is always null, so the ui wont update.
This is my model code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MovieApp.Models
{
public class MovieModel : INotifyPropertyChanged
{
string original_title, overview;
public event PropertyChangedEventHandler PropertyChanged;
public string Original_Title {
get
{
return original_title;
}
set
{
original_title = value;
onPropertyChanged(nameof(Original_Title));
}
}
public string Overview
{
get
{
return overview;
}
set
{
overview = value;
onPropertyChanged(nameof(Overview));
}
}
protected void onPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
//PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The mainview.xaml.cs:
using MovieApp.API;
using MovieApp.Models;
using MovieApp.Processor;
using System.Windows;
namespace MovieApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
//private readonly MovieModel movieModel = new MovieModel();
public MainWindow()
{
InitializeComponent();
ApiCaller.InitializeClient();
// DataContext = movieModel;
}
private async void previousImageButton_Click(object sender, RoutedEventArgs e)
{
int id = 484718;
await MovieProcessor.LoadMovie(id);
}
private async void nextImageButton_Click(object sender, RoutedEventArgs e)
{
int id = 527774;
await MovieProcessor.LoadMovie(id);
}
}
}
and the maindwindow.xaml:
<Window x:Class="MovieApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MovieApp.Models"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:MovieModel x:Key="movieModel" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button x:Name="previousImageButton" Padding="15" Margin="15" Click="previousImageButton_Click">Previous</Button>
<StackPanel Grid.Row="1">
<TextBlock Text="{Binding Source={StaticResource movieModel}, Path=Original_Title}" ></TextBlock>
<TextBlock Text="{Binding Source={StaticResource movieModel}, Path=Overview }"></TextBlock>
</StackPanel>
<Button Grid.Row="2" x:Name="nextImageButton" Padding="15" Margin="15" Click="nextImageButton_Click">Next</Button>
</Grid>
</Window>
EDIT:
Added the movieprocessor code:
using MovieApp.API;
using MovieApp.Models;
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace MovieApp.Processor
{
class MovieProcessor
{
public static async Task<MovieModel> LoadMovie(int id)
{
string url = $"movie/{id}?api_key=77e7d2ef687aedca2119680778f1d619&language=en-US";
using (HttpResponseMessage response = await ApiCaller.httpClient.GetAsync(url))
{
if (response.IsSuccessStatusCode)
{
MovieModel movie = await response.Content.ReadAsAsync<MovieModel>();
Console.WriteLine(movie.Original_Title);
Console.WriteLine(movie.Overview);
return movie;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
}
}
I have no idea what could be wrong. I tried multiple things but nothing seemed to work for me. I tried adding datacontext but that didnt work either. I let it commented in my code so anyone can see it.
If your MovieProcess class sets the value of MovieModel.Original_Title and MovieModel.Overview property then you have to ensure that MovieProcess is accessing the same instance of MovieModel as your view (xaml).
Instead of using StaticResource movieModel assing DataContext in code behind.
private readonly MovieModel movieModel = new MovieModel();
public MovieProcessor MovieProcessor { get; set; }
public MainWindow()
{
InitializeComponent();
ApiCaller.InitializeClient();
DataContext = movieModel;
MovieProcessor = new MoviewProcessor(moviewModel);
}
Xaml
<StackPanel Grid.Row="1">
<TextBlock Text="{Binding Original_Title}" />
<TextBlock Text="{Binding Overview }" />
</StackPanel>
MovieProcessor class
public class MovieProcessor
{
private readonly MovieModel movieModel;
public MovieProcessor(MovieModel movieModel)
{
this.movieModel = movieModel;
}
public async Task LoadMovie(int id)
{
...
movieModel.Original_Title = <loaded_movie_title>;
movieModel.Overview = <loaded_movie_overview>;
...
}
}
I bind the command to the Buttons in the MovieProcessor to show data in Maindwindow with StaticResource movieModel, below is my code:
NotifyObject.cs
public class NotifyObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChange(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MyCommand.cs
public class MyCommand : ICommand
{
private Func<object, bool> _canExecute;
private Action<object> _execute;
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
{
CommandManager.RequerySuggested += value;
}
}
remove
{
if (_canExecute != null)
{
CommandManager.RequerySuggested -= value;
}
}
}
public bool CanExecute(object parameter)
{
if (_canExecute == null) return true;
return _canExecute(parameter);
}
public void Execute(object parameter)
{
if (_execute != null && CanExecute(parameter))
{
_execute(parameter);
}
}
public MyCommand(Action<object> execute) : this(execute, null)
{
}
public MyCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
}
MovieProcessor.cs
public class MovieProcessor:NotifyObject
{
private MovieModel vm;
public MovieModel VM
{
get { return vm; }
set
{
vm = value;
OnPropertyChange("VM");
}
}
public MovieModel LoadMovie(int id)
{
//....
}
private MyCommand _cmd1;
public MyCommand Cmd1
{
get
{
if (_cmd1 == null)
_cmd1 = new MyCommand(new Action<object>
(
o =>
{
int id = 484718;
LoadMovie(id);
}
));
return _cmd1;
}
}
private MyCommand _cmd2;
public MyCommand Cmd2
{
get
{
if (_cmd2 == null)
_cmd2 = new MyCommand(new Action<object>
(
o =>
{
int id = 527774;
LoadMovie(id);
}
));
return _cmd2;
}
}
}
MainWindow.xaml
<Window.Resources>
<local:MovieProcessor x:Key="movieModel" />
</Window.Resources>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button x:Name="previousImageButton" Padding="15" Margin="15" Command="{Binding Source={StaticResource movieModel}, Path=Cmd1}">Previous</Button>
<StackPanel Grid.Row="1">
<TextBlock Text="{Binding Source={StaticResource movieModel}, Path=VM.Original_Title}" ></TextBlock>
<TextBlock Text="{Binding Source={StaticResource movieModel}, Path=VM.Overview }"></TextBlock>
</StackPanel>
<Button Grid.Row="2" x:Name="nextImageButton" Padding="15" Margin="15" Command="{Binding Source={StaticResource movieModel}, Path=Cmd2}">Next</Button>
</Grid>
I'm having an issue where binding a textbox seems to desync from the underlying property. The first time the underlying property is updated, the textbox does not update. Then, if the underlying property is updated again, the textbox is updated with the original update. This sliding window behavior seems to continue over time. Am I doing something stupid?
I'm using .NET 4.7, vs 2017 community
MainWindow.xaml
<Window x:Class="TextBoxBugTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TextBoxBugTest"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width="225">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<TextBox Width="55" Height="23" Text="{Binding Test}"/>
<TextBox x:Name="test2" Grid.Column="1" Width="55" Height="23"/>
<Button Grid.Row="1" Grid.ColumnSpan="2" Height="23" Width="50"
Click="Button_Click" Content="update"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
MainWindowVM _vm;
private int counter = 1;
public MainWindow()
{
InitializeComponent();
_vm = new MainWindowVM();
DataContext = _vm;
test2.Text = "test";
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var current = counter++;
_vm.Test = $"test{current}";
test2.Text = $"test{current}";
}
}
MainWindowVM.cs
public class MainWindowVM : INotifyPropertyChanged
{
private string _test;
public string Test
{
get { return _test; }
set
{
if (value != _test) on_prop_changed();
_test = value;
}
}
public MainWindowVM()
{
Test = "test";
}
public event PropertyChangedEventHandler PropertyChanged;
private void on_prop_changed([CallerMemberName] string prop = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
Yes, I am such an idiot.
private string _test;
public string Test
{
get { return _test; }
set
{
if (value != _test) on_prop_changed();
_test = value;
}
}
should be
private string _test;
public string Test
{
get { return _test; }
set
{
if (value != _test)
{
_test = value;
on_prop_changed();
}
}
}
I have a List Box in my WPF app. Here is the xaml code:
<ListBox Grid.Row="4" Grid.Column="1" Visibility="{Binding lbIsVisible}">
<ListBoxItem>
<CheckBox>
<TextBlock>CITRUS EXPRESS</TextBlock>
</CheckBox>
</ListBoxItem>
<ListBoxItem>
<CheckBox>
<TextBlock>APL CALIFORNIA</TextBlock>
</CheckBox>
</ListBoxItem>
<ListBoxItem>
<CheckBox>
<TextBlock>IS JAPAN</TextBlock>
</CheckBox>
</ListBoxItem>
</ListBox>
<CheckBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" x:Name="chkSelectVessel" Checked="chkSelectVessel_Checked">
<TextBlock Text="Select Vessel"></TextBlock>
</CheckBox>
I'm trying to toggle the visibility of the list box.
Here is the C# code.
public partial class ConfigSetting : Window
{
public string lbIsVisible { get; set; }
public ConfigSetting()
{
InitializeComponent();
DataContext = this;
lbIsVisible = "Hidden";
}
private void chkSelectVessel_Checked(object sender, RoutedEventArgs e)
{
this.lbIsVisible = "Visible";
}
}
But it doesn't seem to work. Can any one point where I'm going wrong?
You should use INotifyPropertyChanged Interface like this:
public partial class ConfigSetting : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string lbIsVisible;
public string LbIsVisible
{
get { return lbIsVisible; }
set
{
if (lbIsVisible != value)
{
lbIsVisible = value;
OnPropertyChanged("LbIsVisible");
}
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public ConfigSetting()
{
InitializeComponent();
LbIsVisible = "Hidden";
DataContext = this;
}
private void chkSelectVessel_Checked(object sender, RoutedEventArgs e)
{
LbIsVisible = "Visible";
}
private void ChkSelectVessel_OnUnchecked(object sender, RoutedEventArgs e)
{
LbIsVisible = "Hidden";
}
}
And in the XAML bind to LbIsVisible property:
<ListBox Visibility="{Binding LbIsVisible}">
Also add Unchecked event to your CheckBox :
<CheckBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Center"
x:Name="chkSelectVessel" Checked="chkSelectVessel_Checked" Unchecked="ChkSelectVessel_OnUnchecked">
I have a Windows 8 application (Xaml and C#):
I have a userControl that is included in a MainPage.
In this MainPage i have included a BottomAppBar that is fully functioning on RightClick and Windows+Z.
What i need to do is to open the BottomAppBar from an event handler (in the usercontrol back code) on an image that exists in the userControl. i need to access the BottomAppBar in order to use the property IsOpen, but i am not being able to do so. Any hint? am i missing anything?
I am giving you simplest example, which may guide you how to do it.
Normal Way
BlankPage4.xaml
<Page.BottomAppBar>
<AppBar IsSticky="True" IsOpen="True">
<Button Style="{StaticResource BackButtonStyle}" />
</AppBar>
</Page.BottomAppBar>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<local:MyUserControl1 />
</Grid>
MyUserControl1.xaml
<Grid>
<Button Click="btnClose_CLick" Content="Close AppBar" />
</Grid>
MyUserControl1.xaml.cs
private void btnClose_CLick(object sender, RoutedEventArgs e)
{
var isOpen = ((AppBar)((BlankPage4)((Grid)this.Parent).Parent).BottomAppBar).IsOpen;
if (isOpen)
{
((AppBar)((BlankPage4)((Grid)this.Parent).Parent).BottomAppBar).IsOpen = false;
}
else
{
((AppBar)((BlankPage4)((Grid)this.Parent).Parent).BottomAppBar).IsOpen = true;
}
}
MVVM Way
BlankPage4.xaml
<Page.BottomAppBar>
<AppBar IsSticky="True" IsOpen="{Binding IsOpenBottomBar}">
<Button Style="{StaticResource BackButtonStyle}" />
</AppBar>
</Page.BottomAppBar>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<local:MyUserControl1 />
</Grid>
MyUserControl1.xaml.cs
private void btnClose_CLick(object sender, RoutedEventArgs e)
{
var isOpen = (this.DataContext as ViewModel).IsOpenBottomBar;
if (isOpen)
{
(this.DataContext as ViewModel).IsOpenBottomBar = false;
}
else
{
(this.DataContext as ViewModel).IsOpenBottomBar = true;
}
}
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
private bool _IsOpenBottomBar;
public bool IsOpenBottomBar
{
get
{
return _IsOpenBottomBar;
}
set
{
_IsOpenBottomBar = value;
OnPropertyChanged("IsOpenBottomBar");
}
}
public ViewModel()
{
_IsOpenBottomBar = true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I have a WPF textbox with a binding to the datacontext.
<TextBox Grid.Column="1" Grid.Row="4" Text="{Binding Path=Density,UpdateSourceTrigger=PropertyChanged}"/>
I set the datacontext in the code of a the container control of the textbox (tabItem in this case)
tiMaterial.DataContext = _materials[0];
I also have a listbox with other materials. I want to update the textfield, when another material is selected, hence I code:
private void lbMaterials_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
_material = (Material) lbMaterials.SelectedValue;
tiMaterial.DataContext = _material;
}
The Material class implements the INotifyPropertyChanged interface. I have the two-way update working, it's just when I change the DataContext, the bindings seem to be lost.
What am I missing?
I tryed to do what you describe in your post but sincerely I didn’t find the problem. In all the cases that I tested my project works perfectly.
I don’t like your solution because I think that MVVM is more clear, but your way works too.
I hope this helps you.
public class Material
{
public string Name { get; set; }
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
Materials = new Material[] { new Material { Name = "M1" }, new Material { Name = "M2" }, new Material { Name = "M3" } };
}
private Material[] _materials;
public Material[] Materials
{
get { return _materials; }
set { _materials = value;
NotifyPropertyChanged("Materials");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
gridtext.DataContext = (lbox.SelectedItem);
}
}
.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="gridtext">
<TextBlock Text="{Binding Name}" />
</Grid>
<ListBox x:Name="lbox"
Grid.Row="1"
ItemsSource="{Binding Materials}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>