MVVM WPF DataBinding troubleshooting - c#

In following the MVVM architecture for WPF, learning WPF DataBindings. I have a single instance of an object instantiated at runtime with the XAML code <p:MemoryPersistentStorageBridge x:Key="persistentMemoryBridge" /> within Window Resources. I am trying to obtain data from the object instance, and plop it into a TextBox as an example, but I am not getting any text in that text box.
XAML:
<Window x:Class="UserConsole.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:p="clr-namespace:PollPublicDataStock;assembly=PollPublicDataStock"
xmlns:local="clr-namespace:UserConsole"
Title="MainWindow" Height="900" Width="800">
<Window.Resources>
<p:MemoryPersistentStorageBridge x:Key="persistentMemoryBridge" />
</Window.Resources>
<Grid Name="grid1" >
<!-- layout defintions -->
<TextBox DataContext="{StaticResource persistentMemoryBridge}" Text="{Binding Path=GetConnectionString}" Margin="0,327,31,491" Foreground="Black" Background="Yellow"/>
</Grid>
</Window>
CodeBehind:
public class MemoryPersistentStorageBridge {
public MemoryPersistentStorageBridge() {
}
public string GetConnectionString() {
return "THISTEXTSHOULDAPPEARINTEXTBOXBUTSADLYDOESNOT";
}
}

You are trying to bind to a method. You need to bind to a property. Or else use an ObjectDataProvider.
So you could do this:
public class MemoryPersistentStorageBridge {
public MemoryPersistentStorageBridge() {
}
public string ConnectionString {
get { return GetConnectionString(); }
}
public string GetConnectionString() {
return "THISTEXTSHOULDAPPEARINTEXTBOXBUTSADLYDOESNOT";
}
}
or even:
public class MemoryPersistentStorageBridge {
public MemoryPersistentStorageBridge() {
}
public string ConnectionString {
get { return "THISTEXTSHOULDAPPEARINTEXTBOXBUTSADLYDOESNOT"; }
}
}
Of course, in either case we are not handling changing the property and notifying the binding of a change.
The other option is to use the ObjectDataProvider to wrap your method. This is illustrated in the link I provided. But would look something like this:
<ObjectDataProvider ObjectInstance="{StaticResource persistentMemoryBridge}"
MethodName="GetConnectionString" x:Key="connectionString">
</ObjectDataProvider>

Related

How to build a custom control using recursion?

I am trying to build a rather complex custom control.
I want to use recursion in my View Model to build out a control. I will try and be as clear as possible.
I have two classes, Publisher and Developer
The Publisher class looks like the following:
public class Publisher
{
public Publisher()
{
SubPublishers.CollectionChanged += SubPublishers_CollectionChanged;
ChildDevelopers.CollectionChanged += SubPublishers_CollectionChanged;
}
private ObservableCollection<Publisher> subPublishers;
public ObservableCollection<Publisher> SubPublishers
{
get
{
if (subPublishers == null)
subPublishers = new ObservableCollection<Publisher>();
return subPublishers;
}
}
private ObservableCollection<Developer> childDevelopers;
public ObservableCollection<Developer> ChildDevelopers
{
get
{
if (childDevelopers == null)
childDevelopers = new ObservableCollection<Developer>();
return childDevelopers;
}
}
And my Developer Class looks like this:
public class Developer : NotifyPropertyChanged
{
public Developer(string Title)
{
this.Title = Title;
}
private string title;
public string Title
{
get
{
return this.title;
}
set
{
this.title = value;
OnPropertyChanged("Title");
}
}
So yes, Publisher is n-tier. It can have a Collection of Developers and each of these Developers can have their own Collection of Developers.
Going to my Main View Model:
public class MainViewModel : NotifyPropertyChanged
{
public MainViewModel()
{
this.ParentPublisher = new ParentPublisher();
BuildData();
}
private Publisher parentPublisher;
public Publisher ParentPublisher
{
get
{
return this.parentPublisher;
}
set
{
this.parentPublisher = value;
OnPropertyChanged("ParentPublisher");
}
}
public void BuildData()
{
Publisher firstPublisher = new Publisher();
firstPublisher.ChildDevelopers.Add(new Developer("HAL"));
firstPublisher.ChildDevelopers.Add(new Developer("Retro Games"));
firstPublisher.ChildDevelopers.Add(new Developer("Nintendo"));
Publisher secondPublisher = new Publisher();
secondPublisher.ChildDevelopers.Add(new Developer("343"));
secondPublisher.ChildDevelopers.Add(new Developer("Playground Games"));
secondPublisher.SubPublishers.Add(new Publisher());
secondPublisher.SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Coalition"));
secondPublisher.SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Remedy"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.Add(new Publisher());
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Insomniac"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Criterion"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("EA"));
ParentPublisher.Add(firstPublisher);
ParentPublisher.Add(secondPublisher);
}
}
}
So, you can see the possible scenarios here. Now, I was trying to figure out how to build a control off this data.
I want to actually bind to the ParentPublisher because everything added (SubPublishers and the Child Developers) will ultimately be extension of the Parent.
Would I use an ObservableCollection and use the ItemSource to this ParentPublisher?
Any tips or recommendations would be appreciated.
One way is to make a UserControl that excepts the first tier as a DataTemplate. Then you show the data in the DataTemplate as needed. Inside the DataTemplate reference the UserControl again with the inner data.
Example: Obviously modify the layout to benefit you but for simplicity I did it this way.
<UserControl x:Class="WPF_Question_Answer_App.PublisherView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF_Question_Answer_App"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate x:Key="DeveloperTemplate">
<TextBlock Text="{Binding Title}" /> <!--show the developer data-->
</DataTemplate>
<DataTemplate x:Key="PublisherTemplate">
<local:PublisherView /> <!-- reference ourself for recursion-->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding ChildDevelopers}"
ItemTemplate="{StaticResource DeveloperTemplate}" />
<ItemsControl ItemsSource="{Binding SubPublishers}"
ItemTemplate="{StaticResource PublisherTemplate}" />
</StackPanel>
</UserControl>

Window property and Checkbox databinding xaml

I have been a few hours now trying to understand how to do data-binding.
Initially I was following some examples but they all show to do the databinding using {Binding Source={StaticResource myObject}, Path=myObject.myProperty}
or {Binding Path=myObject.myProperty}
Nothing of this seem to bind the Config object inside the controller that is inside the Window.
If I do the binding as an StaticResource it does the binding to an object of the Controller class but is NOT the object that is created inside the window class, this Config seems to be a new separate instance. This is the part I don't understand. If someone could explain or give me some reference where to look I would greatly appreciate it.
This is some code very simplified
Window1.cs
<Window x:Class="Sample.UI.Main"
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:controller="clr-namespace:Sample.Controller"
mc:Ignorable="d"
Title="SampleApp" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<controller:PublisherController x:Key="oController" />
</ResourceDictionary>
</Window.Resources>
<CheckBox x:Name="chkBoxShowRoom" Style="{StaticResource checkBoxTemplate}" Content="{StaticResource configShowRoom}" IsChecked="{Binding Source={StaticResource oController}, Path=Config.ShowRoom}"/>
Then my Window1.cs
public partial class Main : Window
{
public PublisherController Controller { get; set; }
Then Controller.cs
public class PublisherController
{
public Configuration Config { get; set; }
Then the Configuration.cs
public class Configuration : AbstractEntity, INotifyPropertyChanged
{
private bool _ShowRoom;
public bool ShowRoom
{
get
{
return _ShowRoom;
}
set
{
if (value != _ShowRoom)
{
this._ShowRoom = value;
OnPropertyChanged();
}
}
}
...

Can't read read-only property even with OneWay mode set

ViewModel
namespace My.ViewModels
{
public class ItemViewModel : ObservableObject
{
private ItemModel _model;
public ItemViewModel(ItemModel model)
{
_model = model;
}
public string Name { get { return _model.Name; } }
}
}
XAML
<UserControl x:Class="My.Controls.ItemControl"
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"
xmlns:viewModels="clr-namespace:My.ViewModels"
mc:Ignorable="d"
d:DesignHeight="421" d:DesignWidth="786"
d:DataContext="{d:DesignInstance viewModels:ItemViewModel}">
<Grid Background="White">
<TextBlock><Run Text="Name:" /> <Run Text="{Binding Name, FallbackValue=Name, Mode=OneWay}" /></TextBlock>
</Grid>
</UserControl>
Error:
A TwoWay or OneWayToSource binding cannot work on the read-only property 'Name'
I'm trying to do DataBinding to a read-only property from my ViewModel.
I've set the Binding Mode to OneWay.. yet it still throws the error above.
I'm out of clues! Any help would be appreciated.
To use OneWay binding your property should has get and set. So in this case, to fix the problem you just add set to your property like this:
public string Name { private set; get { return _model.Name; }

How to access a static variable from code behind? [duplicate]

This question already has answers here:
Setting Label Text in XAML to string constant
(3 answers)
Closed 9 years ago.
Question: Is it possible to access a static variable from code behind to be used in XAML?
Reason: I want a single string variable to keep a menu name which will be used in different places (in code behind and also in XAML).
Example (code behind):
public partial class MainWindow : Window
{
public static readonly string menuName = "MyMenu";
... other code ...
}
Example (XAML):
<MenuItem Header="... here I want my menuName to appear ..." />
for that you would need to instantiate your class in xaml once, then you can use the static member.
it would be better to create a separate class for static variables and load it in xaml in resources.
something like this
<Window.Resources>
<!-- Create an instance of the class called MyClass -->
<my:MyClass x:Key="MyClass" />
</Window.Resources>
then use it as something like
<TextBox Text="{x:Static my:MyClass.MyProperty}" Width="500" Height="100" />
or
<TextBlock Text="{Binding Source={StaticResource MyClass},Path=MyProperty}" />
also see
XAML Binding to static classes
How to bind in XAML to a static property?
You should add it to project Resource dictionary:
go to you project -> Properties -> Resources-> Add Resource Button
then you can use it in Xaml or code behind like that:
-- XAML---
<MenuItem Header="{x:Static properties:Resources.menuName}" />
--- Code behind ----
Properties.Resources.menuName
you can not do this for the simple reason that when you bind on your property you will get an infinite nested calls to MainWindow which will generate an 'System.StackOverflowException' you should use a container class like this
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
}
public class WindowMessagesManager
{
private static string _header;
public static string Header1
{
get { return "My Header"; }
set { _header = value; }
}
}
}
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prop="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<prop:WindowMessagesManager x:Key="window" ></prop:WindowMessagesManager>
//you can try to uncomment this and you will get an exception
<!--<prop:MainWindow x:Key="window"></prop:MainWindow>-->
</Window.Resources>
<Grid>
<Menu>
<MenuItem Height="100" Width="100" Header="{Binding Source={StaticResource ResourceKey=window}, Path=Header1}"></MenuItem>
</Menu>
</Grid>
</Window>

Creating a custom image class in WPF following MVVM pattern

Im just starting out with MVVM and at the moment still find alot of things confusing.
So I am trying to keep things as simple as I can at the moment.
I am trying to write code for a custom image which later will be able to be placed on a canvas control by a user at runtime. I'm trying to use MVVM so that I will be able to save and reload the content on a canvas.
I have created a model class called CustomImage with the following code:
namespace StoryboardToolMvvm
{
public class CustomImage
{
public Uri imageLocation { get; set; }
public BitmapImage bitmapImage { get; set; }
}
}
I have a modelview class as follows:
namespace StoryboardToolMvvm
{
class CustomImageViewModel : ViewModelBase
{
private CustomImage _customImage;
private ObservableCollection<CustomImage> _customImages;
private ICommand _SubmitCommand;
public CustomImage CustomImage
{
get { return _customImage; }
set
{
_customImage = value;
NotifyPropertyChanged("CustomImage");
}
}
public ObservableCollection<CustomImage> CustomImages
{
get { return _customImages; }
set
{
_customImages = value;
NotifyPropertyChanged("CustomImages");
}
}
public ICommand SubmitCommand
{
get
{
if (_SubmitCommand == null)
{
_SubmitCommand = new RelayCommand(param => this.Submit(), null);
}
return _SubmitCommand;
}
}
public CustomImageViewModel()
{
CustomImage = new CustomImage();
CustomImages = new ObservableCollection<CustomImage>();
CustomImages.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(CustomImages_CollectionChanged);
}
private void CustomImages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("CustomImages");
}
private void Submit()
{
CustomImage.imageLocation = new Uri(#"H:\My Pictures\whale.png");
CustomImage.bitmapImage = new BitmapImage(CustomImage.imageLocation);
CustomImages.Add(CustomImage);
CustomImage = new CustomImage();
}
}
}
And a view class:
<UserControl x:Class="StoryboardToolMvvm.CustomImageView"
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"
xmlns:viewmodel="clr-namespace:StoryboardToolMvvm"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<viewmodel:CustomImageViewModel x:Key="CustomImageViewModel"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource CustomImageViewModel}}">
<Image Source="{Binding CustomImage.bitmapImage, Mode=TwoWay}" Width="150" Height="150" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="75,50,0,0" />
<Button Content="Submit" Command="{Binding SubmitCommand}" Width="100" Height="50" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,20" />
</Grid>
</UserControl>
I add this view to my MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StoryboardToolMvvm" x:Class="StoryboardToolMvvm.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CustomImageView HorizontalAlignment="Left" Height="100" Margin="181,110,0,0" VerticalAlignment="Top" Width="100"/>
</Grid>
</Window>
I am very unsure as to whether I am on the right lines here with a MVVM pattern so any comments would be much appreciated. Also when Submit is pressed I would have expected my image to load but this does not happen can anyone advise as to why?
Many Thanks in advance..
As far as my understanding of MVVM and your question goes, I have one main comment about your code.
I think your CustomImage is actually both Model and ViewModel layer, and you should split it in two :
the Model, which would contain the path itself ;
the ViewModel, which contain the BitmapImage and initialize it from the Model and constructing time.
The path is the mere data used for saving, and it fits the Model, whereas the BitmapImage is how the data is shown and should be constructed in the ViewModel.
One advantage is that now, your BitmapImage gets its own NotifyPropertyChanged call at setting time, and you won't have anymore problem or a View part directly bound to the Model.
As for your CustomImageViewModel, this looks like more of a MainViewModel-ish thing. You can still use this to store the ViewModels.

Categories

Resources