Fill color from property via XAML - wpf

I am learning WPF and have this simple question.
How do I set fill color to property vi XAML?
<Rectangle Fill="{Binding Path=BackgroundColorf}"
Height="112" Margin="0,84,0,0" VerticalAlignment="Top"
Width="116"/>
public partial class MainWindow : Window
{
/// <summary>
/// Gets or sets the BackgroundColor.
/// </summary>
public SolidColorBrush BackgroundColorf
{
get;
set;
}
public MainWindow()
{
this.InitializeComponent();
BackgroundColorf = new SolidColorBrush(Colors.Red);
}
}

Set the datacontext like this
public MainWindow()
{
this.DataContext = this;
this.InitializeComponent();
BackgroundColorf = new SolidColorBrush(Colors.Red);
}
This should work.But there is little more to be done for making your wpf app scalable like Notifications,Dependency properties etc.I recommend you go through the basics of wpf DataBinding architecture before continuing.Go through the link posted by H.B in the comments

To get you going ...
Add a name to Rectangle
<Rectangle x:Name="MyRect" Fill="{Binding Path=BackgroundColorf}" Height="112" ...
then in the code
InitializeComponent();
MyRect.DataContext = this;
BackgroundColorf = new SolidColorBrush(Colors.Red);
Not the best way of doing things - but at least you'll have a red rectangle :)

if you add this your example will work
public MainWindow()
{
this.InitializeComponent();
this.DataContext = this;
BackgroundColorf = new SolidColorBrush(Colors.Red);
}
but you should really in some wpf books or websites to get the basics.
a very good Book is "WPF 4 Unleashed" from Adam Nathan.

Related

DataContext of usercontrol in WPF

I'm new to WPF and I'm trying to start a little project with a maximum of good practice. I'm using MVVM and dependency injection.
I have a concern which seems to be easy to understand but i can't find an answer (at this step, DataContext is not very clear for me).
The UserControlView of type UserControl contains just a button for testing.
This is the app class :
public App()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<MainWindow>();
services.AddSingleton<UserControlViewModel>();
services.AddSingleton<UserControlView>();
_serviceProvider = services.BuildServiceProvider();
}
The user control is included in the Main windows like that :
<Grid>
<views:UserControlView/>
</Grid>
Now, in the OnStartup overrided method :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
MainWindow.DataContext = _serviceProvider.GetRequiredService<PaymentMeansViewModel>();
MainWindow.Show();
}
Like that it works, my button is correctly binded to the command.
But what is strange for me is that I have to set the 'UserControlViewModel' as the DataContext of the Main Window.
Isn'it possible to bind it to the 'UserControlView', something like :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
UserControlView testUC = _serviceProvider.GetRequiredService<UserControlView>();
testUC.DataContext = _serviceProvider.GetRequiredService<UserControlViewModel>();
MainWindow.Show();
}
Thanks for help.
Finally I did it.
I think (I hope I'm right) that I understood.
First of all, let's begin with the basic.
A view must have a viewmodel to bind the properties. A usercontrol is a kind of view "encapsulated" in a view. Therefore a usercontrol must have its own viewmodel and the view must have its own viewmodel.
The datacontext of the MainWindow is set in the app onstartup method :
MainWindow = new MainWindow()
{
DataContext = new MainWindowViewModel()
};
MainWindow must implement INotifyPropertyChanged. All view models must implement this interface. We can create a base class which will be derived in the view models :
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string? propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The DataContext of the usercontrol must be explicit in the xaml of the MainWindow:
<Grid>
<views:UserControlView DataContext="{Binding CurrentViewModel}"/>
</Grid>
"CurrentViewModel" is a DataContext, then it's a ViewModel, and as it is binded, it must be a property of the MainViewModel.
public class MainWindowViewModel : ViewModelBase
{
public ViewModelBase CurrentViewModel { get; }
public MainWindowViewModel()
{
CurrentViewModel=new UserControlViewModel();
}
}
Hope it can help.

How to enable DesignTime data in Visual Studio with Unity 4 and MvvmLight

I'm trying to get my head around WPF, Unity and MvvMlight (galasoft). So far my little set up works. If I run my application the label is filled with a random name generated by my DataService. (small victory getting all moving parts to work)
But in the design view of Visual Studio the label remains empty. How do i convince VisualStudio to render some 'design time' data in my label?
I'm using: Visual Studio Premium 2013, Unity 4.0.1, MvvmLight 5.2, .net 4.5
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IDataService, DataService>();
container.RegisterType<IMainViewModel, MainViewModel>();
MainWindow mainWindow = container.Resolve<MainWindow>();
mainWindow.Show();
base.OnStartup(e);
}
}
In App.xaml I have not defined the StartUpUri
MainWindow.xaml
<Window x:Class="UnityMvvmTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="500">
<Grid>
<Label x:Name="myLabel" Content="{Binding MyText}"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindow(IMainViewModel theViewModel)
: this()
{
this.DataContext = theViewModel;
}
}
MainViewModel.cs
public class MainViewModel : ViewModelBase, IMainViewModel
{
private readonly IDataService _dataService;
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
if (IsInDesignMode)
{
// Code runs in design time data.
MyText = "Design Data";
}
else
{
// Code runs "for real"
MyText = _dataService.GetName();
}
}
public string MyText { get; set; }
}
I found a method, using hints from https://stackoverflow.com/a/3380895/249845
I created a second (flat) implementation of IMainVieModel in a separate namespace: UnityMvvmTest.ViewModel.Design. This implementation has no logic, it just fills the properties so the designer has some data to display.
This implementation is used in design time, since it is specified as the DesignTime DataContext. (with xmlns:d, xmlns:mc and xmlns:vm). The mc-namespace is needed to hide the d-namespace during runtime, see why.
The result is 5 extra lines in the Xaml, an extra (almost empty) implementation of IMainViewModel. And an extra (empty) constructor in code behind, instead of a constuctor that test for IsInDesignMode. This isn't a big deal, since unity will pick the constructor with the most parameters it can resolve.
MainWindow.xaml
<Window x:Class="UnityMvvmTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:UnityMvvmTest.ViewModel.Design"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type=vm:MainViewModel}"
mc:Ignorable="d"
>
<Grid>
<Label x:Name="myLabel" Content="{Binding MyText}" />
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
// Contructor used in DesignTime
public MainWindow()
{
InitializeComponent();
}
//Constructor used by Unity
public MainWindow(IMainViewModel theViewModel)
: this()
{
this.DataContext = theViewModel;
}
}
MainViewModel.cs (design time implementation)
namespace UnityMvvmTest.ViewModel.Design
{
public class MainViewModel : IMainViewModel
{
public MainViewModel()
{
MyText = "my Design time data";
}
public string MyText { get; set; }
}
}

WPF Data binding to observableCollection

Can someone give me a little help with this one please
I'm trying to reorganise an app to MVVM and make better use of data binding, but am struggling with a little issue.
I have a viewmodel class
public class MainWindowViewModel
{
public ObservableCollection<DiagramElement> Elements { get; set; }
public MainWindowViewModel()
{
AppMachineList = new ListOfMachines();
Elements = new ObservableCollection<DiagramElement>();
}
}
in which I create an observablecollection of the DiagramElement class.
public class DiagramElement : Button
{
private Item linkedItem;
public Item LinkedItem
{
get { return this.linkedItem; }
set
{
this.linkedItem = value;
this.DataContext = this;
this.Template = (ControlTemplate)FindResource("ItemTemplate");
}
}
The DiagramElement class just extends the button class and adds its own controlTemplate.
Back in my MainWindow.xaml.cs class, I instantiate the viewmodel and from that, populate a stackpanel in MainWindow.xaml from the ObservableCollection.
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
MainWindowViewModel vm = new MainWindowViewModel();
vm.LoadMachines();
foreach(DiagramElement d in vm.Elements)
{
ItemList.Children.Add(d);
}
}
}
<StackPanel x:Name="ItemList" Orientation="Vertical"></StackPanel>
What I want to do is, do away with the foreach loop and the calls to ItemList.Children.Add(). And replace this with a binding to Elements in the viewmodel like below.
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
MainWindowViewModel vm = new MainWindowViewModel();
vm.LoadMachines();
this.DataContext = vm;
}
}
<StackPanel x:Name="ItemList" DataContext="{Binding Path=Elements}"</Stackpanel>
I can't get the elements to be added to the Stackpanel, the binding doesn't work. Any help gratefully received.
FYI, having a ViewModel with a collection of UI elements (in your case, buttons) violates the principles of MVVM -- the UI and model should not be co-mingled like this.
But the immediate problem is you cannot use a StackPanel -- it is a control container but does not support binding to lists of items. You need to use some kind of repeater like an ItemsControl.
<ItemsControl ItemsSource="{Binding Path=Elements}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- your DiagramElement should go here, something like
<DiagramElement LinkedItem={Binding Path=SomePropertyOnYourRevisedElement} />
-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But there's more work to be done. DiagramElement needs to have LinkedItem converted into a DependencyProperty (lots of examples of this online) and Elements needs to be a list of some sort of model object that just stores the properties needed for the DiagramElement (with no UI stuff).

Simple WPF data binding

I want to separate my user interface from my code, so I (obviously) landed at bindings. As a test, I've written the following XAML:
<Window x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="Auto" Width="200">
<StackPanel>
<TextBox Text="{Binding Item}"/>
<Button Content="Add" Click="AddNew"/>
<ListBox Height="100" ItemsSource="{Binding Items}"/>
</StackPanel>
</Window>
The C# looks like this:
namespace BindingTest
{
public partial class MainWindow : Window
{
public string Item { get; set; }
public ObservableCollection<string> Items { get; set; }
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<string>();
}
private void AddNew(object sender, RoutedEventArgs e)
{
Items.Add(Item);
}
}
}
What I want to happen is that the text entered into the textbox is added to the listbox's itemssource. However, this doesn't happen...
Two things you need two do -
Set - DataContext = this; in your constructor.
You'd be better off if you would change your properties to dependency properties instead. You could do that easily with the "propdp" snippet in visual studio.
Data binding is performed against the current data context. However, you have not set the data context for your window. Often you will set the data context to a view model but in your case you simply want to use the window class for that.
You should add the following line to the constructor:
DataContext = this;
Change your code to this:
public partial class MainWindow : Window
{
public string Item { get; set; }
public ObservableCollection<string> Items { get; set; }
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<string>();
DataContext = this;
}
private void AddNew(object sender, RoutedEventArgs e)
{
Items.Add(Item);
}
}
}
You do need to set your DataContext - works for me.
Two things:
You should set the correct data context for your window. Otherwise the binding will not find your properties.
You should initialize your Items collection before the InitializeComponent() call as inside it the ListBox tries to evaluate the expression and get NULL as the binding souce. And since you are not implementing INotifyPropertyChanged and the property is not a DependencyProperty the ListBox will never reevaluate the binding thus it will never get the instance of your Items collection.
So, the code should be as follows:
public MainWindow()
{
Items = new ObservableCollection<string>();
DataContext = this;
InitializeComponent();
}
Try this
hope this will work. But this is not hte right approach. You need to set the DataContext to the Object whose properties u guna use for binding. you must follow MVVM Architecture.

Cannot display records in DataGrid

I am just new to WPF and I am having problems displaying my record. It seems that my records are "shy" when it comes to displaying them, even though I have all my records already.
Code for my App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Window1 window = new Window1();
var viewModel = new Window1ViewModel();
window.PHGrid.ItemsSource = viewModel.ViewModels;
window.Show();
}
Code for Window1ViewModel:
public class Window1ViewModel : ViewModelBase
{
private readonly DAPHContrib _contribRepository;
private ObservableCollection<ViewModelBase> _viewModelBases;
public ObservableCollection<ViewModelBase> ViewModels
{
get
{
if (_viewModelBases == null)
{
_viewModelBases = new ObservableCollection<ViewModelBase>();
}
return _viewModelBases;
}
}
public Window1ViewModel()
{
_contribRepository = new DAPHContrib();
//Create instance of our view model to add it in our collection
PHContribViewModel viewModel = new PHContribViewModel(_contribRepository);
ViewModels.Add(viewModel);
}
}
Here's my Window1.xaml UPDATED:
<Window x:Class="Wabby_App.Views.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Wabby_App.ViewModels"
xmlns:v="clr-namespace:Wabby_App.Views"
Title="Utos ng mahal ko"
Height="300"
Width="300">
<Grid>
<DataGrid
AutoGenerateColumns="True"
Height="200"
HorizontalAlignment="Center"
Name="PHGrid"
VerticalAlignment="Center"
Width="200"
ItemsSource="{Binding ViewModels}"/>
</Grid>
Output:
Hope you can help me with this.
based on your comments you have view models collection (ObservableCollection<ViewModelBase>)
and inside each of these view model base instances (PHContribViewModel) you have another collection ObservableCollection<PHContrib_Entity>.
Hence you have two levels of nested collections and one datagrid to map. This wont work as it is. For this you would need to flatten this 2 level hierarchy of collections into one list of type ObservableCollection<PHContrib_Entity>.
Use LINQ to do that...
protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
Window1 window = new Window1();
var viewModel = new Window1ViewModel();
window.PHGrid.ItemsSource
= viewModel.ViewModels.SelectMany(vm => vm.PHContribEntities).ToList();
window.Show();
}
Let me know if this helps...
Your View (Window1) is not binding to ViewModel, it's just setting the control's ItemsSource to a property of the ViewModel which is an incorrect way to implement MVVM. What you need to do is set DataContext of Window1 to instance of ViewModel (Bind View to ViewModel). So, you need to update your code in the OnStartup method.
from
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Window1 window = new Window1();
var viewModel = new Window1ViewModel();
window.PHGrid.ItemsSource = viewModel.ViewModels;
window.Show();
}
to
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Window1 window = new Window1();
var viewModel = new Window1ViewModel();
window.DataContext = viewModel;
window.Show();
}
Update
You also need to set ItemsSource property of datagrid to property in ViewModel
<DataGrid ItemsSource={Binding ViewModels} ..

Resources