I have grid like this:
<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{Binding}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="CurrentPoints" Grid.Row="1" Grid.Column="0"
Text="{Binding CurrentPoints}" Width="100" Height="100" Foreground="Red">
</TextBlock>
<TextBlock x:Name="PointsLeft" Grid.Row="1" Grid.Column="1"
Text="{Binding AllPoints}" Width="100" Height="100" Foreground="Red" />
<TextBlock x:Name="AllPoints" Grid.Row="1" Grid.Column="2"
Text="{Binding EndPoints}" Grid.ColumnSpan="1"/>
</Grid>
Code for page like this:
Counter pocitadlo = new Counter(200);
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
LayoutRoot.DataContext = pocitadlo;
}
And this is my counter class:
class Counter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _currentPoints;
public int CurrentPoints
{
get
{
return _currentPoints;
}
set
{
this._currentPoints = value;
onPropertyChanged(this, "CurrentPoints");
}
}
private int _allPoints;
public int AllPoints
{
get
{
return _allPoints;
}
set
{
this._allPoints = value;
onPropertyChanged(this, "AllPoints");
}
}
private int _endPoints;
public int EndPoints
{
get
{
return _endPoints;
}
set
{
this._endPoints = value;
onPropertyChanged(this, "EndPoints");
}
}
public Counter(int endPoints)
{
this._allPoints = 0;
this._currentPoints = 0;
this._endPoints = endPoints;
}
private void onPropertyChanged(object sender, string property)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(sender, new PropertyChangedEventArgs(property));
}
}
}
Why isn´t binding working? What I am missing? Thanks
Related
I have a counter which increments depending on a foreach Loop from:
public partial class UserControlCounter : UserControl, INotifyPropertyChanged
{
private int _scanStatusCounter;
public int ScanStatusCounter
{
get { return _scanStatusCounter; }
set { _scanStatusCounter = value; NotifyPropertyChanged(); }
}
public UserControlCounter()
{
InitializeComponent();
DataContext = this;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(getAll);
scanStatus.Text = "Persons " + ScanStatusCounter.ToString();
}
private async void getAll()
{
//grab data and iterate it
string[] string_array = new string[] { "a", "b", "c", "d", "e" }; // type = System.String[]
foreach (var i in string_array)
{
ScanStatusCounter++;
await Task.Delay(100);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and the Xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Grid.Column="0" Name="myListBox" Height="40" ></ListBox>
<TextBlock Grid.Row="0" Grid.Column="1" Name="scanStatus" Height="40" Text="{Binding Path=ScanStatusCounter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<Button Grid.Row="0" Grid.Column="2" Click="Button_Click" Height="40">Click Me</Button>
</Grid>
That works fine and increments "live", but my problem is that I need to get that now running inside a more complex Data Template.
I've tried to inject the Counter onClick with:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
var contentPresenter = (btn.TemplatedParent as ContentPresenter);
var ppStatusCounter = contentPresenter.ContentTemplate.FindName("scanStatus", contentPresenter) as TextBlock;
ppStatusCounter.Text = "Entrys found: " + ScanStatusCounter.ToString();
}
But then I get the Value only onClick not like a live incrementing Counter, that's my Data Template:
<UserControl.Resources>
<c1:NameList x:Key="NameListData"/>
<DataTemplate x:Key="NameItemTemplate">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Domaine"/>
<TextBox Grid.Row="1" Grid.Column="0" x:Name="txtDomainName" Text="{Binding Path=DomaineName}" Margin="0,0,5,0"></TextBox>
<Button Grid.Row="1" Grid.Column="1" Width="100" Height="30" Margin="5,5,5,5" x:Name="btnNames" Click="Button_Click" Content="Start Scan" />
<Grid Grid.Column="2" Grid.Row="1" Height="20">
<ProgressBar x:Name="pbStatus" Height="20" Width="100" Minimum="0" Maximum="100" Visibility="Hidden"/>
<TextBlock x:Name="pbStatusText" HorizontalAlignment="Center" VerticalAlignment="Center" Text="Scanning" Visibility="Hidden"/>
</Grid>
<TextBlock Grid.Column="3" Grid.Row="1" Name="scanStatus" Text="{Binding Path=ScanStatusCounter, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="top">
<StackPanel Orientation="Horizontal" Margin="0,20,0,0">
<ListBox x:Name="lstDomainNames"
Margin="5,5,5,5"
ItemsSource="{Binding Source={StaticResource NameListData}}"
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
</StackPanel>
</StackPanel>
</Grid>
that's my Data Source Class, not much there:
public partial class NameList : ObservableCollection<SetCredentials>
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public NameList() : base()
{
using var forest = Forest.GetCurrentForest();
Forest currentForest = Forest.GetCurrentForest();
DomainCollection domains = currentForest.Domains;
foreach (Domain objDomain in domains)
{
Add(new SetCredentials(objDomain.ToString()));
}
}
}
public class SetCredentials
{
private string domainName;
public SetCredentials(string domain)
{
this.domainName = domain;
}
public string DomaineName
{
get { return domainName; }
set { domainName = value; }
}
}
Do I have to add a second Data Binding source to my ItemSource or do I need another approach for this, for example in my TextBox Binding?
I'm trying to dev a WPF app which displays 3 views that we can switch using a side menu.
The first View displays articles. I've tried this view in another project on its own and it works just fine.
My issue:
The problem is that when I use it in my navigation (with the exact same code) the Images or Titles won't appear unless I click on another view first then click back.
Here's my current code:
Model: (Article)
public class Article : INotifyPropertyChanged
{
int id;
string title;
string link;
BitmapImage image;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Article(int id, string title, string link, BitmapImage image)
{
Id = id;
Title = title;
Link = link;
Image = image;
}
public int Id
{
get => id;
set { if (value != id) { id = value; NotifyPropertyChanged(); } }
}
public string Title
{
get => title;
set { if (value != title) { title = value; NotifyPropertyChanged(); } }
}
public string Link
{
get => link;
set { if (value != link) { link = value; NotifyPropertyChanged(); } }
}
public BitmapImage Image
{
get => image;
set { if (value != image) { image = value; NotifyPropertyChanged(); } }
}
}
ViewModel: (ArticlesViewModel)
public class ArticlesViewModel : INotifyPropertyChanged
{
Article[] articles;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public AccueilViewModel()
{
loadArticle();
}
public Article[] Articles
{
get => articles;
set { if (value != articles) { articles = value; NotifyPropertyChanged(); } }
}
private async void loadArticle()
{
var client = new WordPressClient(App.WordpressLink);
try
{
var asychArticles = GetArticles(client);
await asychArticles;
var articles = asychArticles.Result;
int i = 0;
int a;
foreach (WordPressPCL.Models.Post p in articles)
{
a = p.FeaturedMedia.Value;
var media = GetMedia(client, a);
await media;
BitmapImage bimage = App.getImage(media.Result.SourceUrl);
articleImg[i].Source = bimage;
articleTitle[i].Content = p.Title.Rendered.ToUpper();
articleLink[i] = p.Link;
i++;
if (i == 4) break;
}
}
catch (System.Net.Http.HttpRequestException e)
{
}
}
static async Task<IEnumerable<WordPressPCL.Models.Post>> GetArticles(WordPressClient client)
{
var posts = await client.Posts.GetAll();
return posts;
}
static async Task<WordPressPCL.Models.MediaItem> GetMedia(WordPressClient client, int number)
{
var media = await client.Media.GetByID(number);
return media;
}
}
View: (ArticlesView)
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<Grid Name="Article1" Grid.Row="0" Margin="10,10,10,10" DataContext="{Binding Articles[0]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image1" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre1" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Name="Article2" Grid.Column="0" Margin="10,10,10,10" DataContext="{Binding Articles[1]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image2" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre2" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Name="Article3" Grid.Column="1" Margin="10,10,10,10" DataContext="{Binding Articles[2]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image3" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre3" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Name="Article4" Grid.Column="2" Margin="10,10,10,10" DataContext="{Binding Articles[3]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image4" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre4" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
</Grid>
</Grid>
MainWindow.xaml:
<Window.Resources>
<DataTemplate x:Name="ArticlesViewTemplate" DataType="{x:Type viewmodels:ArticlesViewModel}">
<views:ArticlesView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Feature2ViewTemplate" DataType="{x:Type viewmodels:Feature2ViewModel}">
<views:Feature2View DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Feature3ViewTemplate" DataType="{x:Type viewmodels:Feature3ViewModel}">
<views:Feature3View DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel Grid.Row="1" VerticalAlignment="Top" Margin="20,20,20,0">
<Button Content="Articles" Click="F1Button_Click"/>
<Button Content="F2" Click="F2Button_Click"/>
<Button Content="F3" Click="F3Button_Click"/>
</StackPanel>
</Grid>
<ContentControl Grid.Column="1" Content="{Binding}"/>
</Grid>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
ArticlesViewModel avm;
Feature2ViewModel f2vm;
Feature3ViewModel f3vm;
public MainWindow()
{
InitializeComponent();
}
private void F1Button_Click(object sender, RoutedEventArgs e)
{
if (avm == null) avm = new AccueilViewModel();
DataContext = avm;
}
private void F2Button_Click(object sender, RoutedEventArgs e)
{
if (f2vm == null) f2vm = new CatalogueViewModel();
DataContext = f2vm;
}
private void F3Button_Click(object sender, RoutedEventArgs e)
{
if (f3vm == null) f3vm = new MesFormationsViewModel();
DataContext = f3vm;
}
}
To sum up:
Click on Articles -> Nothing is displayed
Click on F2 then Articles -> Nothing is displayed
Click on Articles then F2 then Articles -> Images and Titles correctly displayed!
Any ideas on where the problem might be coming from? Thank you.
So the problem seemed to be coming from the fact that I was using an array. At the start the DataContext was null so the images and titles even though they were loaded in the modelview were not displayed on the view.
The problem was fixed by using an ObservableCollection instead of the array. If I understood correctly, NotifyPropertyChanged() is apparently not notifying the view when the array is at first null.(?)
Is there any way to get the index of the data which is binded to the ItemsControl, when a button is clicked?
<ItemsControl x:Name="ic_schranke" DataContext="{Binding Schranke}" >
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Height="80" x:Name="btn_open" Click="btn_open_Click" HorizontalAlignment="Stretch" Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Rectangle x:Name="rect" Grid.ColumnSpan="3" Grid.Row="1" Opacity="0.65" Grid.Column="0" Fill="#FFCEEAFF"/>
<Border CornerRadius="25" Height="50" Width="50" HorizontalAlignment="Center" Grid.Column="0" Grid.Row="1">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name" Grid.Column="1" Grid.Row="1" Text="{Binding name}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="ExtraLight" Foreground="Black" />
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the sample data which is binded to the IC:
List<Schranke> elements;
public UserPage()
{
this.InitializeComponent();
elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { name = $"Schranke NR:{i}" });
this.ic_schranke.ItemsSource = elements;
}
And here is the code in the button click event:
private async void btn_open_Click(object sender, RoutedEventArgs e)
{
Grid grid = (sender as Button).Content as Grid;
//Debug.WriteLine($"content {tb.Text} clicked");
var tmp_background = grid.Background;
grid.Background = new SolidColorBrush(new Windows.UI.Color() { A = 255, R = 78, G = 170, B = 44 });
VibrationDevice testVibrationDevice = VibrationDevice.GetDefault();
testVibrationDevice.Vibrate(System.TimeSpan.FromMilliseconds(150));
await Task.Delay(3000);
grid.Background = tmp_background;
}
Maybe something along the lines of this:
the presenter - put this in the data context
public class SchrankePresenter : INotifyPropertyChanged
{
private List<Schranke> _elements;
public List<Schranke> Elements
{
get { return _elements; }
set
{
_elements = value;
OnPropertyChanged("Elements");
}
}
public ICommand ClickCommand { get; set; }
private void OnPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
public SchrankePresenter()
{
var elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { Name = $"Schranke NR:{i}" });
Elements = elements;
ClickCommand = new DelegateCommand(ClickAction);
}
public void ClickAction(Schranke item)
{
VibrationDevice.GetDefault().Vibrate(TimeSpan.FromMilliseconds(150));
}
}
public class Schranke
{
public string Name { get; set; }
}
the template:
<ListView ItemsSource="{Binding Elements}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Height="80"
HorizontalAlignment="Stretch"
Command="{Binding ClickCommand}"
Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="30*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Rectangle x:Name="rect"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Fill="#FFCEEAFF"
Opacity="0.65" />
<Border Grid.Row="1"
Grid.Column="0"
Width="50"
Height="50"
HorizontalAlignment="Center"
CornerRadius="25">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name"
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="ExtraLight"
Foreground="Black"
Text="{Binding Name}" />
</Grid>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
you can do the background animation using a storyboard/visualstate
EDIT : regarding the DelegateCommand, it's a simple implementation I use for my WPF apps -
internal class DelegateCommand<T> : ICommand
{
private Action<T> _clickAction;
public DelegateCommand(Action<T> clickAction)
{
_clickAction = clickAction;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_clickAction((T)parameter);
}
}
When developing MVVP program i collide with some problem based on unsensibility of class when it's properties changed.
For example we have base class Brick, that have properties inditating Number of Holes and Cracks in it.
class Class_Brick : DependencyObject
{
public Class_Brick()
{
}
#region DependencyProperties
public static DependencyProperty NumberOfHoles_Property = DependencyProperty.Register("NumberOfHoles", typeof(int), typeof(Class_Brick),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty NumberOfCracks_Property = DependencyProperty.Register("NumberOfCracks", typeof(int), typeof(Class_Brick),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
#endregion
#region Properties
public int NumberOfHoles
{
get { return (int)GetValue(NumberOfHoles_Property); }
set { SetValue(NumberOfHoles_Property, value); }
}
public int NumberOfCracks
{
get { return (int)GetValue(NumberOfCracks_Property); }
set { SetValue(NumberOfCracks_Property, value); }
}
#endregion
}
Class Wall has collection of Bricks and Standard Brick (put both in DependencyProperty is not obligatory - only to demonstrate needed callback).
Also Wall has same properties as Brick that calculate Number of Cracks and Holes in collection of Bricks and Standard Brick. Tey must calculate every time, when changed parametres in Standard Brick or changed collection (add item, remove item or changed parametres in item).
class Class_Wall : DependencyObject
{
public Class_Wall()
{
SetValue(Standard_Brick_Property, new Class_Brick());
SetValue(Bricks_Property, new ObservableCollection<Class_Brick>());
}
#region DependencyProperties
public static DependencyProperty NumberOfHoles_Property = DependencyProperty.Register("NumberOfHoles", typeof(int), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty NumberOfCracks_Property = DependencyProperty.Register("NumberOfCracks", typeof(int), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty Standard_Brick_Property = DependencyProperty.Register("Standard_Brick", typeof(Class_Brick), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged,
PropertyChangedCallback = new System.Windows.PropertyChangedCallback(On_Property_Change)
});
public static DependencyProperty Bricks_Property = DependencyProperty.Register("Bricks", typeof(ObservableCollection<Class_Brick>), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged,
PropertyChangedCallback = new System.Windows.PropertyChangedCallback(On_Property_Change)
});
#endregion
#region Inner Methods
public static void On_Property_Change(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ObservableCollection<Class_Brick> inWall;
int wallCracks = 0;
int wallHoles = 0;
if (e.Property == Bricks_Property)
{
inWall = e.NewValue as ObservableCollection<Class_Brick>;
Class_Brick mainItem = (Class_Brick)d.GetValue(Standard_Brick_Property);
wallCracks = mainItem.NumberOfCracks;
wallHoles = mainItem.NumberOfHoles;
}
else //if (e.Property == Standard_Brick_Property)
{
inWall = (ObservableCollection<Class_Brick>)d.GetValue(Bricks_Property);
Class_Brick newItem = e.NewValue as Class_Brick;
wallCracks = newItem.NumberOfCracks;
wallHoles = newItem.NumberOfHoles;
}
if (inWall != null)
{
foreach (Class_Brick brick in inWall)
{
wallCracks += brick.NumberOfCracks;
wallHoles += brick.NumberOfHoles;
}
d.SetValue(Home_Quest.Class_Wall.NumberOfCracks_Property, wallCracks);
d.SetValue(Home_Quest.Class_Wall.NumberOfHoles_Property, wallHoles);
}
}
#endregion
#region Properties
public int NumberOfHoles
{
get { return (int)GetValue(NumberOfHoles_Property); }
}
public int NumberOfCracks
{
get { return (int)GetValue(NumberOfCracks_Property); }
}
public Class_Brick Standard_Brick
{
get { return (Class_Brick)GetValue(Standard_Brick_Property); }
set { SetValue(Standard_Brick_Property, value); }
}
public ObservableCollection<Class_Brick> Bricks
{
get { return (ObservableCollection<Class_Brick>)GetValue(Bricks_Property); }
set { SetValue(Bricks_Property, value); }
}
#endregion
}
I used this XAML for the test field:
<DockPanel LastChildFill="True">
<TextBlock Text="Wall"
DockPanel.Dock="Top"
HorizontalAlignment="Center"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Column="0"
Grid.Row="0" Grid.RowSpan="2"
LastChildFill="True">
<TextBlock Text="Bricks"
DockPanel.Dock="Top" />
<Button Content="-" DockPanel.Dock="Bottom"
Click="btn_Remove" />
<Button Content="+" DockPanel.Dock="Bottom"
Click="btn_Add" />
<ListBox x:Name="lB_Bricks"
ItemsSource="{Binding Path=Bricks}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="2"
BorderBrush="Azure"
BorderThickness="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles, Mode=TwoWay}"
Grid.Column="1" Grid.Row="0"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks, Mode=TwoWay}"
Grid.Column="1" Grid.Row="1"
TextAlignment="Center"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<DockPanel Grid.Column="1"
Grid.Row="0"
LastChildFill="True"
DataContext="{Binding Path=Standard_Brick}">
<TextBlock Text="Standard Brick"
DockPanel.Dock="Top" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles, Mode=TwoWay}"
Grid.Column="1" Grid.Row="0"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks, Mode=TwoWay}"
Grid.Column="1" Grid.Row="1"
TextAlignment="Center"/>
</Grid>
</DockPanel>
<DockPanel Grid.Column="1"
Grid.Row="1"
LastChildFill="True">
<TextBlock Text="Resulted Cracks and Holes"
DockPanel.Dock="Top" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles}"
Grid.Column="1" Grid.Row="0"
IsReadOnly="True"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks}"
Grid.Column="1" Grid.Row="1"
IsReadOnly="True"
TextAlignment="Center"/>
</Grid>
</DockPanel>
</Grid>
</DockPanel>
and code for MainWindow.cs:
public MainWindow()
{
InitializeComponent();
this.DataContext = currentWall;
}
Class_Wall currentWall = new Class_Wall();
private void btn_Add(object sender, RoutedEventArgs e)
{
currentWall.Bricks.Add(new Class_Brick());
}
private void btn_Remove(object sender, RoutedEventArgs e)
{
Class_Brick selectedBrick = this.lB_Bricks.SelectedItem as Class_Brick;
if ( selectedBrick != null)
{
currentWall.Bricks.Remove(selectedBrick);
}
}
Until today, I never got to find the solution for this problem, using DependencyProperties.
Hope for your help and support.
Well, INotifyPropertyChanged helps solve my problem.
Here some code for updated Classes Brick:
class Class_Brick_NPC : INotifyPropertyChanged
{
public Class_Brick_NPC()
{
}
#region Event Functions
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
#region Inner Parametres
private int numberOfHoles = 0;
private int numberOfCracks = 0;
#endregion
#region Parametres
public int NumberOfHoles
{
get { return numberOfHoles; }
set
{
numberOfHoles = value;
OnPropertyChanged("NumberOfHoles");
}
}
public int NumberOfCracks
{
get { return numberOfCracks; }
set
{
numberOfCracks = value;
OnPropertyChanged("NumberOfCracks");
}
}
#endregion
}
and Wall:
class Class_Wall_NPC : INotifyPropertyChanged
{
public Class_Wall_NPC()
{
standard_Brick.PropertyChanged += InnerUpdate;
}
#region Event Methods
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public void InnerUpdate(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "NumberOfHoles")
{
OnPropertyChanged("NumberOfHoles");
}
else if (args.PropertyName == "NumberOfCracks")
{
OnPropertyChanged("NumberOfCracks");
}
}
#endregion
#region Inner Properties
private ObservableCollection<Class_Brick_NPC> bricks = new ObservableCollection<Class_Brick_NPC>();
private Class_Brick_NPC standard_Brick = new Class_Brick_NPC();
private int numberOfHoles = 0;
private int numberOfCracks = 0;
#endregion
#region Properties
public ObservableCollection<Class_Brick_NPC> Bricks
{
get { return bricks; }
set
{
bricks = value;
OnPropertyChanged("Bricks");
}
}
public Class_Brick_NPC Standard_Brick
{
get { return standard_Brick; }
set
{
standard_Brick = value;
}
}
public int NumberOfHoles
{
get
{
int wallHoles = standard_Brick.NumberOfHoles;
foreach (Class_Brick_NPC brick in bricks)
{
wallHoles += brick.NumberOfHoles;
}
return wallHoles;
}
}
public int NumberOfCracks
{
get
{
int wallCracks = standard_Brick.NumberOfCracks;
foreach (Class_Brick_NPC brick in bricks)
{
wallCracks += brick.NumberOfCracks;
}
return wallCracks;
}
}
#endregion
#region Methods
public void Add_Brick(Class_Brick_NPC addedItem)
{
bricks.Add(addedItem);
addedItem.PropertyChanged += InnerUpdate;
OnPropertyChanged("Bricks");
}
public void Remove_Brick(Class_Brick_NPC removedItem)
{
bricks.Remove(removedItem);
removedItem.PropertyChanged -= InnerUpdate;
OnPropertyChanged("Bricks");
OnPropertyChanged("NumberOfHoles");
OnPropertyChanged("NumberOfCracks");
}
#endregion
}
But is there any way to achieve same result using DependencyProperty?
As the title says, im trying to display the hardcoded values I typed in my XAML file through databindings but in 1 method call instead of binding the textboxes in every specific property.
Model:
public class Person
{
public string Namn { get; set; }
public DateTime Födelsedatum { get; set; }
public int Betyg { get; set; }
public int AntalBarn { get; set; }
public int Favoritsiffra { get; set; }
public string Kommentar { get; set; }
}
View:
<Window x:Class="PiedPiper.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Inlämningsuppgift WPF" Height="400" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Content="Personuppgifter" FontSize="35" Grid.Column="0" Grid.Row="0"></Label>
<Separator Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="10,2,-50,2"/>
<Label Content="Namn:" Grid.Column="0" Grid.Row="2" Margin="5,5,210,0"></Label>
<TextBox Name="NamnTextBox" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"/>
<Label Content="Födelsedatum:" Grid.Column="0" Grid.Row="3" Margin="5,5,210,0"></Label>
<TextBox Name="FödelsedatumTextBox" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"></TextBox>
<Label Content="Betyg" Grid.Column="0" Grid.Row="4" Margin="5,5,210,0"></Label>
<ComboBox Name="BetygComboBox" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="218,5,-50,0"></ComboBox>
<Label Content="Antal barn (0-20):" Grid.Column="0" Grid.Row="5" Margin="5,5,210,0"></Label>
<Slider Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,25,0"></Slider>
<TextBox Name="AntalBarnTextBox" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="300,5,-50,0"></TextBox>
<Label Content="Favoritsiffra (0-99):" Grid.Column="0" Grid.Row="6" Margin="5,5,210,0"></Label>
<TextBox Name="FavoritsiffraTextBox" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Margin="225,5,-50,0"></TextBox>
<CheckBox Content="Visa kommentar" Grid.Row="7" Grid.Column="0" Margin="5,5,210,0"></CheckBox>
<TextBox Name="KommentarTextBox" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,-50,-50" Grid.RowSpan="2"></TextBox>
<Button Content="Spara" Grid.Row="8" Grid.Column="0" Margin="125,55,-50,-80" Grid.ColumnSpan="2"></Button>
<Separator Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Margin="10,85,-50,-80"></Separator>
<Label Content="Andre Kordasti" Grid.Row="10" Grid.Column="0" Margin="10,85,-50,-80"></Label>
<Button Content="Avsluta" Grid.Row="10" Grid.Column="0" Margin="225,85,-50,-80" Grid.ColumnSpan="2"></Button>
</Grid>
ViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel(Person person)
{
Namn = person.Namn;
Födelsedatum = person.Födelsedatum;
Betyg = person.Betyg;
AntalBarn = person.AntalBarn;
Favoritsiffra = person.Favoritsiffra;
Kommentar = person.Kommentar;
GetPerson();
}
private void GetPerson()
{
Namn = "KurtSune";
Födelsedatum = new DateTime(1980, 09, 06);
Betyg = 3;
AntalBarn = 7;
Favoritsiffra = 10;
Kommentar = "Kommentar...";
var mainWindow = new MainWindow();
mainWindow.NamnTextBox.Text = Namn;
mainWindow.FödelsedatumTextBox.Text = Convert.ToString(Födelsedatum);
mainWindow.BetygComboBox.SelectedValue = Betyg;
mainWindow.AntalBarnTextBox.Text = Convert.ToString(AntalBarn);
mainWindow.FavoritsiffraTextBox.Text = Convert.ToString(Favoritsiffra);
mainWindow.KommentarTextBox.Text = Kommentar;
}
public ICommand SaveCommand { get; set; }
public ICommand AbortCommand { get; set; }
public bool CanSave
{
get
{
if (Namn == null)
{
return false;
}
return !String.IsNullOrWhiteSpace(Namn);
}
}
private string _namn;
public string Namn
{
get { return _namn; }
set { _namn = value; OnPropertyChanged("Namn"); }
}
private DateTime _födelsedatum;
public DateTime Födelsedatum
{
get { return _födelsedatum; }
set { _födelsedatum = value; OnPropertyChanged("Födelsedatum"); }
}
private int _betyg;
public int Betyg
{
get { return _betyg; }
set { _betyg = value; OnPropertyChanged("Betyg"); }
}
private int _antalBarn;
public int AntalBarn
{
get { return _antalBarn; }
set { _antalBarn = value; OnPropertyChanged("AntalBarn"); }
}
private int _favoritSiffra;
public int Favoritsiffra
{
get { return _favoritSiffra; }
set { _favoritSiffra = value; OnPropertyChanged("Födelsedatum"); }
}
private string _kommentar;
public string Kommentar
{
get { return _kommentar; }
set { _kommentar = value; OnPropertyChanged("Kommentar"); }
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
And to add my App.xaml.cs file:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var person = new Person();
var personViewModel = new PersonViewModel(person);
var mainWindow = new MainWindow();
mainWindow.DataContext = personViewModel;
mainWindow.Show();
}
}
So I dont get the following values to be displayed... Please help, Iam new to this.
Here is a simplified example where,
I made a DataTemplate where controls are bound to the Person properties. Benefits are obvious because as you will see whichever end is modified, the other will know/show these changes.
(added a button and a MessageBox to further demonstrate this)
Code:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var person = new Person {Name = "name1", Address = "address1"};
person.PropertyChanged += person_PropertyChanged;
DataContext = person;
}
private void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MessageBox.Show("data changed");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var dataContext = (Person) DataContext;
dataContext.Name = "newName";
dataContext.Address = "newAddress";
}
}
internal class Person : INotifyPropertyChanged
{
private string _address;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Address
{
get { return _address; }
set
{
_address = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<DataTemplate x:Key="PersonTemplate" DataType="wpfApplication1:Person">
<StackPanel>
<TextBlock Text="Name" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Address" />
<TextBox Text="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Click="ButtonBase_OnClick" Content="Modify" />
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource PersonTemplate}" />
</StackPanel>
</Grid>
</Window>
Now as you can see there are no hard-coded values in XAML, the object I have passed to DataContext has sample values instead.