WPF - Property not binded using DataType - c#

I have a main viewmodel with several child viewmodel
For each childVM, I have definied the following in the main view:
<Window.Resources>
<vm:MainVM x:Key="MainVM" />
<DataTemplate DataType="{x:Type vm:ChildVM}">
<view:childview />
</DataTemplate>
</Window.Resources>
Inside childview there's a textbox with:
<TextBox>
<i:Interaction.Behaviors>
<behavior:AppendTextBehavior AppendTextAction="{Binding AppendTextAction, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
</TextBox>
The behavior class:
public class AppendTextBehavior : Behavior<TextBox>
{
public Action<string> AppendTextAction
{
get { return (Action<string>)GetValue(AppendTextActionProperty); }
set { SetValue(AppendTextActionProperty, value); }
}
public static readonly DependencyProperty AppendTextActionProperty =
DependencyProperty.Register("AppendTextAction", typeof(Action<string>), typeof(AppendTextBehavior));
protected override void OnAttached()
{
base.OnAttached();
SetCurrentValue(AppendTextActionProperty, (Action<string>)AssociatedObject.AppendText);
AssociatedObject.TextChanged += AssociatedObject_TextChanged;
}
void AssociatedObject_TextChanged(object sender, EventArgs e)
{
// does something
}
}
Main view
<TabControl
ItemsSource="{Binding Tabs, Mode=OneWay}"
>
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
</StackPanel>
..
<Button Content="+" Command="{Binding Source={StaticResource MainVM}, Path=AddTabCommand}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl>
Main ViewModel
private ObservableCollection<ChildVM> tabs;
public ObservableCollection<ChildVM> Tabs
{
get
{
return tabs;
}
set
{
tabs = value;
RaisePropertyChanged(() => Tabs);
}
}
void AddTab()
{
ChildVM tab = new ChildVM();
Tabs.Add(tab);
}
public ICommand AddTabCommand { get { return new MvvmFoundation.Wpf.RelayCommand(AddTab); } }
Finally, ChildVM has:
private Action<string> appendTextAction;
public Action<string> AppendTextAction
{
get { return appendTextAction; }
set {
appendTextAction = value;
RaisePropertyChanged(() => AppendTextAction);
}
}
Now, AppendTextAction's VM is always null, so the binding doesn't work.
BUT, if I try to create the childVM within the view like this:
<UserControl.DataContext>
<vm:ChildVM>
</UserControl.DataContext>
The Binding works perfectly!
Obviously the code above is wrong, because I don't need to create the child view model again
What can I do to make the AppendTextAction binding work ?

Related

Pass ObservableCollection<> type as dependency property

I am trying to create a multi-select Combobox Custom control, This custom control should expose a dependency property called DropDownDataSource through which the user of the control can decide what day should bound to ComboBox. My code looks like this:
MainPage.Xaml
<Grid>
<local:CustomComboBox x:Name="customcb" DropDownDataSource="{x:Bind DropDownDataSource, Mode=OneWay}" Loaded="CustomControl_Loaded"> </local:CustomComboBox>
</Grid>
MainPage.Xaml.cs
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private ObservableCollection<Item> _dropDownDataSource;
public ObservableCollection<Item> DropDownDataSource
{
get => _dropDownDataSource;
set
{
_dropDownDataSource = value;
OnPropertyChanged();
}
}
public MainPage()
{
this.InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private void CustomControl_Loaded(object sender, RoutedEventArgs e)
{
var Items = new ObservableCollection<Item>(Enumerable.Range(1, 10)
.Select(x => new Item
{
Text = string.Format("Item {0}", x),
IsChecked = x == 40 ? true : false
}));
DropDownDataSource = Items;
}
}
Models
public class Item : BindableBase
{
public string Text { get; set; }
bool _IsChecked = default;
public bool IsChecked { get { return _IsChecked; } set { SetProperty(ref _IsChecked, value); } }
}
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T storage, T value,
[System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (!object.Equals(storage, value))
{
storage = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
CustomUserControl XAML
<Grid x:Name="GrdMainContainer">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBox Width="200" FontSize="24" Text="{Binding Header, Mode=TwoWay}"
IsReadOnly="True" TextWrapping="Wrap" MaxHeight="200" />
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="200" Width="200" Background="White">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}"
FontSize="24"
Foreground="Black"
IsChecked="{Binding IsChecked, Mode=TwoWay}"
IsThreeState="False" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</Grid>
CustomUserControl Cs file
public sealed partial class CustomComboBox : UserControl
{
public CustomComboBox()
{
this.InitializeComponent();
}
public ObservableCollection<Item> DropDownDataSource
{
get { return (ObservableCollection<Item>)GetValue(DropDownDataSourceProperty); }
set { SetValue(DropDownDataSourceProperty, value); }
}
public static readonly DependencyProperty DropDownDataSourceProperty =
DependencyProperty.Register("DropDownDataSource", typeof(ObservableCollection<Item>), typeof(CustomComboBox), new PropertyMetadata("", HasDropDownItemUpdated));
private static void HasDropDownItemUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CustomComboBox ucrcntrl)
{
var grd = UIElementExtensions.FindControl<Grid>(ucrcntrl, "GrdMainContainer");
grd.DataContext = ucrcntrl.DropDownDataSource as ObservableCollection<Item>;
}
}
}
All looks good to me, but for some reason, Dropdown is coming empty. Instead of the dependency property, If I assign a view model directly to the Control it works fine. But in my condition, it is mandatory that I have properties like DataSource,SelectedIndex, etc on the user control for the end-user to use. Can anyone point out what is going wrong here?
Here, I have attached a copy of my complete code.
I downloaded your sample code, the problem should be in the binding.
<ItemsControl ItemsSource="{Binding Items}">
This way of writing is not recommended. In the ObservableCollection, Items is a protected property and is not suitable as a binding property.
You can try to bind dependency property directly in ItemsControl:
<ItemsControl ItemsSource="{x:Bind DropDownDataSource,Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:Item">
<CheckBox IsChecked="{x:Bind IsChecked, Mode=TwoWay}"
IsThreeState="False" >
<TextBlock Text="{x:Bind Text}" Foreground="Black" FontSize="24"/>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In addition, you may have noticed that I modified the style of CheckBox and rewritten the content to TextBlock, because in the default style of CheckBox, Foreground is not bound to the internal ContentPresenter.
Thanks.

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.

WPF Tabcontrol (TabItem Content is not appearing)

I implemented a TabControl with Closable TabItems in my App. For this, I am using a Collection which I fill with the SubMenuItems of MenuItem "Öffne", which are bound to ICommands in the MainViewModel.
So if I click on MenuItem "Open Tab1", then the header of Tab 1 is created, but I can not see any content. The content of the TabItem is shown AFTER I click on the Header of the TabItem. But I want it to be shown directly when the TabItem is "created" without any need of clicking on the header. Closing the TabItems from the "X" button works fine.
I looked at a couple of examples and tried a ContentTemplate, but it didn't work (Maybe I made something wrong?).
I Hope you can tell me what i have done wrong or show me a good example.
Thanks in advance!
Here are my code snippets:
MainWindow.XAML:
<Window.Resources>
<vm:MainViewModel x:Key="viewModel"/>
</Window.Resources>
<TabControl Background="#FFE5E5E5" ItemsSource="{Binding TabControlViews}" SelectedItem="{Binding CurrentTabItem}" Margin="0,21,0,0">
<TabControl.ItemTemplate>
<DataTemplate>
<DockPanel Width="120">
<TextBlock Text="{Binding Header}"/>
<Button
Command="{Binding ParameterizedCommand, Source={StaticResource viewModel}}"
CommandParameter="{Binding Header, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Courier"
FontSize="9"
FontWeight="Bold"
Margin="0,1,0,0"
Padding="0"
VerticalContentAlignment="Bottom"
Width="16" Height="16" />
<ContentPresenter
Content="{Binding Path=DisplayName}"
VerticalAlignment="Center" />
</DockPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!--<TabControl.ContentTemplate>
<DataTemplate>
</DataTemplate>
</TabControl.ContentTemplate>-->
<TabControl.Resources>
<DataTemplate x:Name="test" DataType="{x:Type vm:MenueVM}">
<cu:MenueSearch/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:FieldPointsVM}">
<cu:FieldPointsSearch/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:DataTransferVM}">
<cu:DataTransfer/>
</DataTemplate>
</TabControl.Resources>
</TabControl>
MainWindow.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = (MainViewModel)Resources["viewModel"];
this.DataContext = vm;
}
}
MainViewModel.cs:
public MainViewModel()
{
TabControlViews = new ObservableCollection<BaseViewModel>();
_menueVM = new MenueVM("Menüpunkte", "Menue");
_fieldVM = new FieldPointsVM("Feldpunkte", "FieldPoint");
_dataVM = new DataTransferVM("DatenTransfer", "DataTransfer");
ParameterizedCommand = new RelayCommand(DoParameterizedCommand);
}
private void DoParameterizedCommand(object parameter)
{
if (parameter.ToString() == "App.ViewModel.MenueVM")
{
TabControlViews.Remove(_menueVM);
}
else if (parameter.ToString() == "App.ViewModel.FieldPointsVM")
{
TabControlViews.Remove(_fieldVM);
}
else if (parameter.ToString() == "App.ViewModel.DataTransfer")
{
TabControlViews.Remove(_dataVM);
}
}
private ICommand _parameterizedCommand;
public ICommand ParameterizedCommand
{
get
{
return _parameterizedCommand;
}
set
{
_parameterizedCommand = value;
}
}
private TabItem _propCurrentTabItem;
public TabItem CurrentTabItem
{
get
{
return _propCurrentTabItem;
}
set
{
_propCurrentTabItem = value;
}
}
private ObservableCollection<BaseViewModel> _TabControlViews = new ObservableCollection<BaseViewModel>();
public ObservableCollection<BaseViewModel> TabControlViews
{
get
{
return _TabControlViews;
}
set
{
_TabControlViews = value;
OnPropertyChanged();
}
}
public ICommand OpenMenupunkteCommand
{
get
{
return new BaseCommand(OpenMenuPunkte);
}
}
public ICommand OpenFeldpunkteCommand
{
get
{
return new BaseCommand(OpenFeldpunkte);
}
}
public ICommand OpenDataTransferCommand
{
get
{
return new BaseCommand(OpenDataTransfer);
}
}
private void OpenMenuPunkte()
{
if (!TabControlViews.Contains(_menueVM))
{
TabControlViews.Add(_menueVM);
}
}
private void OpenFeldpunkte()
{
if (!TabControlViews.Contains(_fieldVM))
{
TabControlViews.Add(_fieldVM);
}
}
private void OpenDataTransfer()
{
if (!TabControlViews.Contains(_dataVM))
{
TabControlViews.Add(_dataVM);
}
}
MenueVM.cs
public class MenueVM : BaseViewModel
{
public MenueVM()
{
//Here are some actions done for Data, but I think they are unimportant for this question
}
public MenueVM(string header, string content)
{
Header = header;
Content = content;
}
private string _header;
public string Header
{
get
{
return _header;
}
set
{
_header = value;
}
}
Still time to post an answer?
Try this :
XAML:
<TabControl ItemsSource="{Binding....}" IsSynchronizedWithCurrentItem="True">
<!-- style, template, ... -->
</TabControl>
CS:
//Adding your viewModel to your ObservableCollection<> TabControlViews
TabControlViews.Add(_viewModelToAdd);
ICollectionView collectionView = CollectionViewSource.GetDefaultView(TabControlViews);
if (collectionView != null)
{
collectionView.MoveCurrentTo(_viewModelToAdd);
//And this is because you don't want your tabItem to be selected :
collectionView.MoveCurrentToPrevious();
}
Found in the downloadable DemoMVVMApp here : https://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030
I've also spent a huge amount of time to solve this problem... ;-)
The problem is that your tab is been created, but it´s not been selected. So, in addition to calling
TabControlViews.Add(_dataVM)
, you should also update your CurrentTabItem
CurrentTabItem = _dataVM;
and bind your TabControl SelectedItem property to your CurrentTabItem, like this:
<TabControl ItemsSource="{Binding TabControlViews}" SelectedItem="{Binding CurrentTabItem}">
Also, if you remove a TabItem and want to get back to the last one, you have to call
CurrentTabItem = TabControlViews.LastOrDefault();

Cannot bind Behaviour property for Autocompletebox

This is a silverlight application, Im using asynchronous filtering for an autocompletebox, the problem is that so far i fail to bind FilterAsyncCommand property from the behaviour with the corresponding ViewModel property.
Following is the xaml declaration for the control in the View:
<controls:ChildWindow x:Class="MyApp.Views.View1"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:fw ="clr-namespace:NetBoxSys.Views"
...
>
<sdk:AutoCompleteBox
MinimumPrefixLength="3" MinimumPopulateDelay="150"
SelectedItem="{Binding Path=...}" ItemsSource="{Binding Path=...}" >
<i:Interaction.Behaviors>
<fw:FilterAsyncBehavior FilterAsyncCommand="{Binding Path=FilterAsyncCommand}" />
</i:Interaction.Behaviors>
</sdk:AutoCompleteBox>
</controls:ChildWindow>
...ViewModel code:
private ICommand filterAsyncCommand;
public ICommand FilterAsyncCommand {
get { return filterAsyncCommand; }
set {
filterAsyncCommand = value;
this.OnPropertyChanged( "FilterAsyncCommand" );
}
}
...and this is how im loading the View
var view = new View1();
view.DataContext = new ViewModel1();
view.Show(); //Modal
I have tried this syntax too but does not work either:
<fw:FilterAsyncBehavior FilterAsyncCommand="{Binding FilterAsyncCommand}" />
Need advice for this kind of binding.
UPDATE:
Behaviour code:
public class FilterAsyncBehavior : Behavior<AutoCompleteBox>
{
public ICommand FilterAsyncCommand
{
get
{
return (ICommand)GetValue(FilterAsyncCommandProperty);
}
set
{
SetValue(FilterAsyncCommandProperty, value);
}
}
public static readonly DependencyProperty FilterAsyncCommandProperty = DependencyProperty.Register("FilterAsyncCommand",
typeof(ICommand),
typeof(FilterAsyncBehavior),
new PropertyMetadata(null));
protected override void OnAttached()
{
base.OnAttached();
// handle the populating event of the associated auto complete box
AssociatedObject.Populating += AssociatedObject_Populating;
}
protected override void OnDetaching()
{
// detach the event handler
AssociatedObject.Populating -= AssociatedObject_Populating;
base.OnDetaching();
}
private void AssociatedObject_Populating(object sender, PopulatingEventArgs e)
{
// get the command
ICommand filterCommand = FilterAsyncCommand;
if (filterCommand != null)
{
// create the parameters for the command
var parameters = new FilterAsyncParameters(AssociatedObject.PopulateComplete, e.Parameter);
// execute command
filterCommand.Execute(parameters);
// cancel the population of the auto complete box
e.Cancel = true;
}
}
}
ViewModel code:
public class ViewModel1 : ViewModel, IViewModel {
public ViewModel1() {
//Initializing the command in constructor
FilterAsyncCommand = new DelegateCommand<FilterAsyncParameters>( ExecuteFilterAsync );
}
private void ExecuteFilterAsync( FilterAsyncParameters args ) {
....
}
private ICommand filterAsyncCommand;
public ICommand FilterAsyncCommand {
get { return filterAsyncCommand; }
set {
filterAsyncCommand = value;
this.OnPropertyChanged( "FilterAsyncCommand" );
}
}
}
I find the solution for my problem. I give up with passing the ViewModel at runtime, so i changed to the following implementation of the MVVM pattern
<controls:ChildWindow x:Class="MyApp.Views.View1"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:fw ="clr-namespace:NetBoxSys.Views"
...
>
<controls:ChildWindow.Resources>
<viewModel:ViewModelRecepcionOCViewModelLocator x:Key="viewModelLocator"/>
</controls:ChildWindow.Resources>
<controls:ChildWindow.DataContext>
<Binding Source="{StaticResource viewModelLocator}" Path="ViewModel" />
</controls:ChildWindow.DataContext>
<sdk:AutoCompleteBox HorizontalAlignment="Stretch" Margin="2" x:Name="remitente_Autocomplete" VerticalAlignment="Center" Grid.Column="1" Grid.Row="4"
MinimumPrefixLength="3" MinimumPopulateDelay="150" Padding="0" Height="23" TabIndex="1" Text="{Binding Path=NombreRemitente,Mode=TwoWay}"
SelectedItem="{Binding Path=SelectedRemitente,Mode=TwoWay}" ItemsSource="{Binding Path=Remitentes}" >
<i:Interaction.Behaviors>
<fw:FilterAsyncBehavior FilterAsyncCommand="{Binding Source={StaticResource viewModelLocator},Path=ViewModel.FilterAsyncCommand}" />
</i:Interaction.Behaviors>
</sdk:AutoCompleteBox>
</controls:ChildWindow>
The important part here is the binding definition for FilterAsyncCommand.
And RecepcionOCViewModelLocator is defined like this
public class RecepcionOCViewModelLocator : ViewModelLocatorBase<IRecepcionOCViewModel>
{
public IRecepcionOCViewModel ViewModel{get;set;}
}

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