How can I bind Foreground to a property in my ViewModel? - c#

I would like to bind the foreground property of a TextBlock to a Property in my ViewModel.
This doesn't work :
Edit
View :
TextBlock
Text="{Binding Path=FullName, Mode=OneWay}"
Foreground="{Binding Path=ForegroundColor}"
Margin="0 5 3 5"
Code behind:
CustomerHeaderViewModel customerHeaderViewModel = new CustomerHeaderViewModel();
customerHeaderViewModel.LoadCustomers();
CustomerHeaderView.DataContext = customerHeaderViewModel;
View Model:
private System.Windows.Media.Brush _foregroundColor;
_foregroundColor = System.Windows.Media.Brushes.DarkSeaGreen;
public System.Windows.Media.Brush ForegroundColor
{
get { return _foregroundColor; }
set { _foregroundColor = value;
OnPropertyChanged("ForegroundColor");
}
}
public CustomerHeaderViewModel()
{
ForegroundColor = System.Windows.Media.Brushes.Red;
}
All other properties (Text etc) correctly bind.
What am I doing wrong?

Check if your solution is like that:
View:
<Window x:Class="WpfApplication13.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:vm="clr-namespace:WpfApplication13"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainVM/>
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding Path=FullName, Mode=OneWay}"
Foreground="{Binding Path=ForegroundColor}"
Margin="0 5 3 5"/>
</Grid>
</Window>
ViewModel:
public class MainVM : INotifyPropertyChanged
{
protected void OnPropertyChanged(string porpName)
{
var temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs(porpName));
}
public event PropertyChangedEventHandler PropertyChanged;
private System.Windows.Media.Brush _foregroundColor = System.Windows.Media.Brushes.DarkSeaGreen;
public string FullName
{
get
{
return "Hello world";
}
}
public System.Windows.Media.Brush ForegroundColor
{
get { return _foregroundColor; }
set
{
_foregroundColor = value;
OnPropertyChanged("ForegroundColor");
}
}
}
and remember that if you want to set new value for ForegroundColor in VM you sholud do it like that:
ForegroundColor = System.Windows.Media.Brushes.Red;
to raise PropertyChangedEvent
Accordind to new information about your problem, you could try this solution:
CustomerHeaderViewModel.cs
class CustomerHeaderViewModel : INotifyPropertyChanged
{
public ObservableCollection<Customer> Customers { get; set; }
public void LoadCustomers()
{
ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
//this is where you would actually call your service
customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23 });
customers.Add(new Customer { FirstName = "Jane", LastName = "Smith", NumberOfContracts = 22 });
customers.Add(new Customer { FirstName = "John", LastName = "Tester", NumberOfContracts = 33 });
customers.Add(new Customer { FirstName = "Robert", LastName = "Smith", NumberOfContracts = 2 });
customers.Add(new Customer { FirstName = "Hank", LastName = "Jobs", NumberOfContracts = 5 });
Customers = customers;
}
protected void OnPropertyChanged(string porpName)
{
var temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs(porpName));
}
public event PropertyChangedEventHandler PropertyChanged;
private System.Windows.Media.Brush _foregroundColor = System.Windows.Media.Brushes.DarkSeaGreen;
public System.Windows.Media.Brush ForegroundColor
{
get { return _foregroundColor; }
set
{
_foregroundColor = value;
OnPropertyChanged("ForegroundColor");
}
}
}
CustomerHeaderView.xaml
<UserControl x:Class="TestMvvm444.Views.CustomerHeaderView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="main">
<Grid>
<StackPanel HorizontalAlignment="Left">
<ItemsControl ItemsSource="{Binding Path=Customers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<TextBox
Text="{Binding Path=FirstName, Mode=TwoWay}"
Width="100"
Margin="3 5 3 5"/>
<TextBox
Text="{Binding Path=LastName, Mode=TwoWay}"
Width="100"
Margin="0 5 3 5"/>
<TextBlock
Text="{Binding Path=FullName, Mode=OneWay}"
Foreground="{Binding ElementName=main, Path=DataContext.ForegroundColor}"
Margin="0 5 3 5"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</UserControl>
In presented scenario the ForegroundColor property resides in CustomerHeaderViewModel.cs so it is value for all customers. In CustomerHeaderView.xaml I added x:Name for UserControl to have a possiblity to refer to DataContext of this element. If you don't want to use x:Name for UserControl, you can try this:
<TextBlock
Text="{Binding Path=FullName, Mode=OneWay}"
Foreground="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}}, Path=DataContext.ForegroundColor}"
Margin="0 5 3 5"/>
Remember that DataContext of this control was set earlier in MainWindow.cs.
MainWindow.cs
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CustomerHeaderViewModel customerHeaderViewModel = new CustomerHeaderViewModel();
customerHeaderViewModel.LoadCustomers();
CustomerHeaderView.DataContext = customerHeaderViewModel;
}
}

This is not a good practice to put UI elements in your view model. Your view model must only encapsulate business locig.
If you want to change the color of anything in your UI that depends on on the value of your textbox, it's a better practice to use data triggers in XAML.
You can do like this :
Viewmodel :
public class MainVm : INotifyPropertyChanged
{
protected void OnPropertyChanged(string porpName)
{
var temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs(porpName));
}
public event PropertyChangedEventHandler PropertyChanged;
public string FullName
{
get { return "Hello world"; }
}
}
XAML (Edited to use the color picker, assuming the selected value of his control is named "SelectedValue" and that it returns a Brush object)
<Grid>
<TextBlock Text="{Binding Path=FullName, Mode=OneWay}"
Margin="0 5 3 5" Foreground="{Binding ElementName=colorpicker, Path=SelectedValue}"/>
<ColorPicker x:Name="colorpicker"/>
</Grid>

Related

Is it possible to bind the foreground color of any textcontrol

So I've been trying to figure this out for 3 days and I just can't seem to find a solution.
This is what I am trying to achieve.
I have a simple WPF project with a RichTextBox in it.
What my application is doing is that it acts like a CMD.
What I want to do now is that I want to change the message it saved when I press enter, I want the previous message to change color.
Here is a GIF showing what it looks like
https://i.imgur.com/srszUKG.gifv
I tried binding the Foreground of the TextBox inside the DataTemplate but that just made it to where the text wouldnt even show up.
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" Name="SavedBlocks" FontFamily="Consolas"/>
</DataTemplate>
So what are my options here, I essentially want to change the color of the text depending on how long the message is.
XAML
<Window x:Class="WpfApp1Eh.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:WpfApp1Eh"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ScrollViewer Name="Scroller" Margin="0" Background="Black">
<StackPanel>
<ItemsControl ItemsSource="{Binding ConsoleOutput, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" Name="SavedBlocks" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox Text="{Binding ConsoleInput, Mode=TwoWay}" Background="Black" Foreground="White" FontFamily="Consolas" Name="InputBlock" BorderBrush="{x:Null}" SelectionBrush="{x:Null}" />
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
main.cs
public partial class MainWindow : Window
{
ConsoleContent dc = new ConsoleContent();
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded1;
DataContext = dc;
}
private void MainWindow_Loaded1(object sender, RoutedEventArgs e)
{
InputBlock.KeyDown += InputBlock_KeyDown;
InputBlock.Focus();
}
void InputBlock_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
dc.ConsoleInput = InputBlock.Text;
dc.RunCommand();
InputBlock.Focus();
Scroller.ScrollToBottom();
}
}
}
ConsoleContent.cs
public class ConsoleContent : INotifyPropertyChanged
{
string consoleInput = string.Empty;
ObservableCollection<string> consoleOutput = new ObservableCollection<string>() { "Console Emulation Sample..." };
public string ConsoleInput
{
get
{
return consoleInput;
}
set
{
consoleInput = value;
OnPropertyChanged("ConsoleInput");
}
}
public ObservableCollection<string> ConsoleOutput
{
get
{
return consoleOutput;
}
set
{
consoleOutput = value;
OnPropertyChanged("ConsoleOutput");
}
}
public void RunCommand()
{
ConsoleOutput.Add(ConsoleInput);
//myBrush = Brushes.Orange;
// do your stuff here.
ConsoleInput = String.Empty;
}
private System.Windows.Media.Brush _foregroundColor = System.Windows.Media.Brushes.DarkSeaGreen;
public System.Windows.Media.Brush ForegroundColor
{
get { return _foregroundColor; }
set
{
_foregroundColor = value;
OnPropertyChanged("ForegroundColor");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Change the type of consoleOutput from ObservableCollection<string> to ObservableCollection<YourType> where YourType is a class that represents a line of input with a text string and a Foreground Brush:
public class YourType : INotifyPropertyChanged
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; OnPropertyChanged("Text"); }
}
private Brush _foreground;
public Brush Foreground
{
get { return _foreground; }
set { _foreground = value; OnPropertyChanged("Foreground"); }
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Bind to the properties of this class in your XAML:
<ScrollViewer Name="Scroller" Margin="0" Background="Black">
<StackPanel>
<ItemsControl ItemsSource="{Binding ConsoleOutput, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"
Foreground="{Binding Foreground}"
Name="SavedBlocks" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox Text="{Binding ConsoleInput, Mode=TwoWay}" Background="Black" Foreground="White" FontFamily="Consolas" Name="InputBlock" BorderBrush="{x:Null}" SelectionBrush="{x:Null}" />
</StackPanel>
</ScrollViewer>
You can then set the Foreground property of each individual item in the source collection:
public void RunCommand()
{
ConsoleOutput.Add(new YourType { Text = ConsoleInput, Foreground = Brushes.Orange } );
ConsoleInput = String.Empty;
}

Selecting TabItem in TabControl from ViewModel

I have been struggling with this for a day or so, can't figure out what I'm doing wrong here. I want to be able to select any tab in my observable collection of tabs, and I want my selection to be visible in the UI. I have tried SelectedIndex and SelectedItem. I can see that my Properties are set but my tabs are not selected, nothing happens in the UI. Here is my code:
MainWindow.xaml
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:WpfApplication5"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<ViewModel xmlns="clr-namespace:WpfApplication5" />
</Window.DataContext>
<StackPanel>
<Button Content="Select Tab Index 0" Click="Button_Click_0"/>
<Button Content="Select Tab Index 1" Click="Button_Click_1"/>
<Label Content="{Binding SelectedIndex, UpdateSourceTrigger=PropertyChanged}" />
<TabControl ItemsSource="{Binding Tabs}" SelectedIndex="{Binding SelectedIndex, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<uc:TabContent Content="{Binding Content}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</StackPanel>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_0(object sender, RoutedEventArgs e)
{
var viewModel = (ViewModel)DataContext;
viewModel.SelectedIndex = 0;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var viewModel = (ViewModel)DataContext;
viewModel.SelectedIndex = 1;
}
}
ViewModel.cs
class ViewModel
{
private int _selectedIndex = 0;
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Tab> _tabCollection = new ObservableCollection<Tab>();
public ViewModel()
{
Tabs.Add(new Tab { Header = "Tab1", Content = new WpfApplication5.TabContent() });
Tabs.Add(new Tab { Header = "Tab2", Content = new WpfApplication5.TabContent() });
}
public ObservableCollection<Tab> Tabs
{
get { return _tabCollection; }
}
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
_selectedIndex = value;
NotifyPropertyChanged("SelectedIndex");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Tab.cs
class Tab
{
public UserControl Content { get; set; }
public string Header { get; set; }
}
TabContent.xaml
<Grid>
<Label Content="Hello World!" />
</Grid>
Your ViewModel class doesn't implement the INotifyPropertyChanged interface:
class ViewModel : INotifyPropertyChanged
{
...
That's your issue.

How to update TextBlock text inside of ListBox item

So I have a simple UDP chat app from a WinForm project, which I wanted to look a little bit better, so I am re-making it in WPF. As I realized I can easily put 2 or more TextBlocks inside of a ListItem, I wanted to display the last message of each chat, like so:
But I have no Idea on how to edit those TextBlocks :( I literary just started with WPF, so I bet I just made a duplicate, but because of that, I don't even know how to search for this issue.
Here is the custom ListBox:
<ListBox x:Name="myList" HorizontalAlignment="Left" Width="264" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0,1,1,0" MouseLeftButtonUp="myList_MouseLeftButtonUp" Margin="0,25,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="0,0,0,1" Width="250">
<DockPanel Margin="0,7">
<Ellipse Name="ellipse" Margin="5" DockPanel.Dock="Left" Style="{DynamicResource elstyle}">
</Ellipse>
<TextBlock Text="{Binding Name}" DockPanel.Dock="Top" Margin="0,0,0,7" FontWeight="Bold" MaxWidth="250"></TextBlock>
<TextBlock Text="{Binding ID}" DockPanel.Dock="Top" Visibility="Hidden" FontSize="1.333"></TextBlock>
<TextBlock x:Name="last_message" Text="{Binding LastMessage}" DockPanel.Dock="Bottom" MaxWidth="250"></TextBlock>
</DockPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is simplified model to show the principal but if you would create view model class that implement INotifyPropertyChanged interface to hold your item data
public class MyItem : INotifyPropertyChanged
{
private string _name;
private string _id;
private string _lastMessage;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string ID
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("ID");
}
}
public string LastMessage
{
get { return _lastMessage; }
set
{
_lastMessage = value;
OnPropertyChanged("LastMessage");
}
}
}
and then in your window
public partial class MainWindow : Window
{
private readonly ObservableCollection<MyItem> _myItems = new ObservableCollection<MyItem>();
public MainWindow()
{
InitializeComponent();
myList.ItemsSource = _myItems;
_myItems.Add(new MyItem { Name = "name", ID = "id", LastMessage = "last message" });
_myItems[0].LastMessage = "new message";
}
}
and then you don't operate on myList control anymore but on _myItems list and its items. If you add/remove item in the collection it will add/remove item in the UI, if you change property of an item it will update bound property in the UI

Binding class to ListView data template

I am trying to bind a ListView with a Model List. The xaml is as -
<ListView Name="lvProductBinding" HorizontalAlignment="Left" Height="434" Margin="10,144,0,0" VerticalAlignment="Top" Width="909">
<ListView.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding ProductNo}" HorizontalAlignment="Left" Margin="967,153,-912,0" VerticalAlignment="Top" Width="895" Height="224" IsExpanded="False">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Height="195" VerticalAlignment="Top" Width="895" Margin="0,0,-2,0">
<Label Content="{Binding ProductDescription}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="181" Height="27" />
<Label Content="{Binding VendorName}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="181" Height="27" />
</StackPanel>
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In mu xaml.cs I am doing this inside the constructor -
List<ProductDetailsModel> products;
products = new List<ProductDetailsModel>();
ProductDetailsModel objProductDetailsModel = new ProductDetailsModel();
objProductDetailsModel.VendorProductInventory = new VendorProductInventory() { ProductNo = "XS-3487", ProductDescription = "Perfume", VendorName = "JohnDoe" };
products.Add(objProductDetailsModel);
objProductDetailsModel = new ProductDetailsModel();
objProductDetailsModel.VendorProductInventory = new VendorProductInventory() { ProductNo = "TT-23487", ProductDescription = "Shoes", VendorName = "Richard Gere" };
products.Add(objProductDetailsModel);
objProductDetailsModel = new ProductDetailsModel();
objProductDetailsModel.VendorProductInventory = new VendorProductInventory() { ProductNo = "VFG-33487", ProductDescription = "Socks", VendorName = "Tom Cruise" };
products.Add(objProductDetailsModel);
lvProductBinding.ItemsSource = products;
where the ProductDetailsModel class is defined as -
public class ProductDetailsModel : INotifyPropertyChanged
{
public ProductDetailsModel()
{
}
private VendorProductInventory _vendorProductInventory;
public VendorProductInventory VendorProductInventory
{
get
{
return _vendorProductInventory;
}
set
{
if (_vendorProductInventory != value)
{
_vendorProductInventory = value;
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, e);
}
}
}
Can someone please advise as to what I am doing wrong here.
Eagerly waiting for a reply.
Thanks,
Saket
There are several things wrong with the code you posted.
First, as Sheridan noted, your implementation of INotifyPropertyChanged is wrong, automatic updates to the data in the ListView won't work until it is correct.
Second, all of the bindings listed in your XAML file don't have matching public properties in the ProductDetailsModel class. More than likely your ListView is being populated and all the bindings are failing (you should see exceptions for this in the VS output window at runtime). Your bindings, as written, should look like:
{Binding Path=VendorProductInventory.ProductNo}

Wpf MVVM - Tabbed interface is not working as expected

First: I am new to MVVM and WPF.
I am trying to create a little application with a tabbed user interface. Users can create products and storage locations, using a button which should open a new TabItem.
My code in the view looks like this:
<TabControl ItemsSource="{Binding Workspaces}"
IsSynchronizedWithCurrentItem="True"
Margin="3"
DockPanel.Dock="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
and the View Model is this:
ObservableCollection<WorkspaceViewModel> _workspaces;
public ObservableCollection<WorkspaceViewModel> Workspaces
{
get
{
if (_workspaces == null)
{
_workspaces = new ObservableCollection<WorkspaceViewModel>();
}
return _workspaces;
}
set
{
_workspaces = value;
}
}
public void AddProduct(object obj)
{
Workspaces.Add(new ProductViewModel());
}
Various other buttons add different ViewModels to the Workspaces Collection.
I have defined multiple Data Template (one for each ViewModel). Here is one:
<DataTemplate DataType="{x:Type vm:ProductViewModel}">
<vw:ProductView />
</DataTemplate>
The WorkspaceViewModel is this:
namespace Inventory.Desktop.ViewModels
{
public abstract class WorkspaceViewModel : INotifyPropertyChanged
{
#region Events and EventHandlers
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
and eg the ProductViewModel
namespace Inventory.Desktop.ViewModels
{
public class ProductViewModel: WorkspaceViewModel
{
private Product _product;
private string _displayName;
public string DisplayName
{
get
{
if (String.IsNullOrEmpty(_displayName))
{
return "Neues Produkt";
} else
{
return _displayName;
}
}
set
{
_displayName = value;
NotifyPropertyChanged("DisplayName");
}
}
#region Public Properties
public Product Product
{
get
{
return _product;
}
set
{
_product = value;
NotifyPropertyChanged("Product");
}
}
public string Title
{
get
{
return _product.Title;
}
set
{
_product.Title = value;
NotifyPropertyChanged("Title");
}
}
public string ScanCode
{
get
{
return _product.ScanCode;
}
set
{
_product.ScanCode = value;
NotifyPropertyChanged("ScanCode");
}
}
public string Manufacturer
{
get
{
return _product.Manufacturer;
}
set
{
_product.Manufacturer = value;
NotifyPropertyChanged("Manufacturer");
}
}
public string ManufacturerNumber
{
get
{
return _product.ManufacturerNumber;
}
set
{
_product.ManufacturerNumber = value;
NotifyPropertyChanged("ManufacturerNumber");
}
}
public string Description
{
get
{
return _product.Description;
}
set
{
_product.Description = value;
NotifyPropertyChanged("Description");
}
}
#endregion
#region Commands
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
return _saveCommand;
}
set
{
_saveCommand = value;
}
}
#endregion
#region Command Executions
public void Save(object obj)
{
using (var db = new InvContext())
{
db.Products.Attach(Product);
db.Entry(Product).State = Product.ProductId == 0 ?
EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
MessageBox.Show("Product saved: " + Product.Title);
}
#endregion
#region Constructors
public ProductViewModel()
{
if (_product == null)
{
_product = new Product();
}
SaveCommand = new RelayCommand(new Action<object>(Save));
}
#endregion
}
}
Here the ProductView.xaml view:
<UserControl x:Class="Inventory.Desktop.Views.ProductView"
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"
d:DesignHeight="400" d:DesignWidth="450">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button Name="SaveProductButton" Command="{Binding SaveCommand}" Content="Speichern" Margin="3" BorderThickness="0">
</Button>
</StackPanel>
<StackPanel DockPanel.Dock="Top" VerticalAlignment="Stretch">
<Label Content="Scan Code" />
<TextBox Text="{Binding Path=ScanCode}" HorizontalAlignment="Stretch" Margin="3" Padding="3" Height="50" TextAlignment="Right">
<TextBox.Background>
<ImageBrush ImageSource="..\Images\Barcode32.png" AlignmentX="Left" Stretch="None" />
</TextBox.Background>
</TextBox>
<Label Content="Bezeichnung" />
<TextBox Text="{Binding Path=Title, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
<Label Content="Hersteller" />
<TextBox Text="{Binding Path=Manufacturer, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
<Label Content="Hersteller Nummer" />
<TextBox Text="{Binding Path=ManufacturerNumber, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
<Label Content="Beschreibung / Information" />
<TextBox Text="{Binding Path=Description, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
</StackPanel>
</DockPanel>
</UserControl>
and here the code-behind ProductView.xaml.cs:
namespace Inventory.Desktop.Views
{
/// <summary>
/// Interaktionslogik für ProductView.xaml
/// </summary>
public partial class ProductView : UserControl
{
ProductViewModel _productModel = new ProductViewModel();
public ProductView()
{
InitializeComponent();
base.DataContext = _productModel;
}
}
}
What's currently working:
When I click a button, I got a new TabItem displaying the correct view and all commands work correctly.
What's not working:
When I open a TabItem, enter some information, and then I open another TabItem with a different ViewModel, switching the focus to the new TabItem and then back to the original oen, then all entered information are gone (object is null).
When I open a TabItem, enter some information, and then I open another TabItem with the same ViewModel, then both TabItems show the the same information.
When I add a new TabItem, it doesn't get focus.
I am totally lost and I hope you can tell me what I am doing wrong.
Best
Stefan
Have a property on your ViewModel to store the reference to current/selected tab
public WorkspaceViewModel SelectedTab
{
get { return _selectedTab; }
set
{
_selectedTab = value;
RaisePropertyChanged(() => SelectedTab);
}
}
and bind this to SelectedItem property on TabControl.
<TabControl ItemsSource="{Binding Workspaces}"
SelectedItem="{Binding SelectedTab, Mode=TwoWay}"
Margin="3"
DockPanel.Dock="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
And finally, you want to update SelectedTab property whenever you are adding a new tab. Modify your AddProduct like this:
public void AddProduct(object obj)
{
var workspace = new ProductViewModel();
Workspaces.Add(workspace);
SelectedTab = workspace;
}

Categories

Resources