I have three tabs. By simply being clicked individually, they will be highlighted individually as they should.
There are RelyCommand behind these tabs. Whenever the mune is clicked, the program should bring back the first TabItem and it should be highlighted. However, when the second tab is clicked, the first tab would not be highlighted as it should, although it behaves like it does get clicked. It is just not highlighted.
Here is the code behind
xaml code for the two tabs at View level:
<StackPanel Orientation="Horizontal"
Background="{x:Null}">
<TabControl Height="50" Margin="12,0,0,0">
<TabItem Name="tiCaptureSetup" IsSelected="{Binding Path=IsCaptureSetupTabSelected, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<TabItem.Header>
<Button Name="btnCaptureSetup"
Grid.Column="0"
Width="90"
Height="40"
Margin="5"
ToolTip="Capture Setup"
Content="Capture Setup"
Click="btnCaptureSetup_Click"
IsEnabled="{Binding Path=CaptureSetupButtonStatus, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
IsDefault="True"
></Button>
</TabItem.Header>
</TabItem>
<TabItem Name="tiCapture" IsSelected="{Binding Path=IsCaptureTabSelected, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<TabItem.Header>
<Button Name="btnCapture"
Grid.Column="0"
Margin="5"
Width="90"
Height="40"
ToolTip="Capture"
Content="Capture"
Click="btnCapture_Click"
IsEnabled="{Binding Path=CaptureButtonStatus, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"></Button>
</TabItem.Header>
</TabItem>
The C# code at ViewModel level (CaptureSetup() is the RelyCommand for clicking the first tab, and HardwareSetupLS() is the RelyCommand for the pop-up window on the menu, and RefereshCaptureSetup() is basically trying to retrieve the first tab when the menu window pops up)
public void CaptureSetup()
{
Command command = new Command();
command.Message = "Capture Setup";
command.CommandGUID = new Guid("6ecb028e-754e-4b50-b0ef-df8f344b668e");
_eventAggregator.GetEvent<CommandShowDialogEvent>().Publish(command);
}
public void HardwareSetupLS()
{
//RefereshCaptureSetup(); // refresh panel when hardware setting window is loaded.
Command command = new Command();
command.Message = "HardwareSetupLS";
command.CommandGUID = new Guid("64c695e6-8959-496c-91f7-5a9a95d91e0d");
_eventAggregator.GetEvent<CommandShowDialogEvent>().Publish(command);
RefereshCaptureSetup();
}
public void RefereshCaptureSetup() // refresh CaptureSetup UI
{
_isCaptureSetupTabSelected = true;
_isCaptureTabSelected = false;
_isReviewTabSelected = false;
Command command = new Command();
command.Message = "Capture Setup";
command.CommandGUID = new Guid("{6ecb028e-754e-4b50-b0ef-df8f344b668e}");
_eventAggregator.GetEvent<CommandShowDialogEvent>().Publish(command);
}
I am very confused at this point what else I can do to make the first TabItem highlighted as it should.
I feel like there is some important logic missing in your question (e.g. how the IsCaptureSetupTabSelected and IsCaptureTabSelected are updated) but anyway here are three pointers from looking at your code:
UpdateSourceTrigger=PropertyChanged is useless since your bindings are OneWay (from the source in your ViewModel towards your UI, the source is never updated). If you have written some logic expected to receive IsSelected change notification upon mouse clicks, this won't happen.
You seem to be updating the inner properties wrapped by your bound properties (e.g. _isCaptureSetupTabSelected = true instead of IsCaptureSetupTabSelected = true ) and thus, could be missing the proper INotifyPropertyChanged event that the UI is expecting.
Make sure that the proper TabItem is on focus.
Related
I added in my code copying the first TabItem along with all the controls. But now how to reference to these controls to make the appropriate modifications (I mean change label text)? Do I must need make new class and binding ?
I want clone tab item to store data in labels about for example other apps.
XAML code:
<TabControl x:Name="MainTabControl" x:FieldModifier="public">
<TabItem x:Name="TabItem1" x:FieldModifier="public" Header="Tab 1">
<Grid>
<Label x:Name="Label1" Content="Test 1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" FontSize="18"/>
<Label x:Name="Label2" Content="Test 2" HorizontalAlignment="Left" Margin="11,44,0,0" VerticalAlignment="Top" FontSize="18"/>
<Button x:Name="Button1" Content="Show Open Dialog" IsEnabled="True" HorizontalAlignment="Left" Margin="11,185,0,0" VerticalAlignment="Top" Width="194" Click="Button1_Click"/>
</Grid>
</TabItem>
C# code:
public MainWindow()
{
InitializeComponent();
mainWindow = this;
TabItem tab2 = TrycloneElement(TabItem1);
if (tab2 != null) MainTabControl.Items.Add(tab2);
}
public static T TrycloneElement<T>(T orig)
{
try
{
string s = XamlWriter.Save(orig);
StringReader stringReader = new StringReader(s);
XmlReader xmlReader = XmlTextReader.Create(stringReader, new XmlReaderSettings());
XmlReaderSettings sx = new XmlReaderSettings();
object x = XamlReader.Load(xmlReader);
return (T)x;
}
catch
{
return (T)((object)null);
}
}
You can access the controls by walking the visual tree using Content, Children or other properties.
TabItem tab2 = TrycloneElement(TabItem1);
var grid = (Grid) tab2.Content;
var label1 = (Label) grid.Children[0];
var label2 = (Label) grid.Children[1];
var button = (Button) grid.Children[2];
An alternative is to use the VisualTreeHelper and convenience helper methods like described here:
How can I find WPF controls by name or type?
Now, these methods work, but you do not need them. You are actually trying to reinvent the wheel. Reusing the same visual structure with different data can be achieved with data templating in a convenient way.
Data Templating Overview
You can use the same technique for your TabControl. I will show you a quick conceptual example that would even comply with the MVVM pattern. This example is not complete. It serves as a base for you to learn about an alternative to your current approach. You can of course also make this approach work in code-behind with a few modifications.
Create a data class like below with your application information. Here I assume that it is not editable, otherwise you need to implement INotifyPropertyChanged in order to update the user interface on changes.
public class AppInfo
{
public AppInfo(string name, string description)
{
Name = name;
Description = description;
}
public string Name { get; }
public string Description { get; }
}
Expose a list of AppInfo (or an ObservableCollection if the collection is changed at runtime).
public List<AppInfo> AppInfos { get; }
Initialize the list appropriately and add your items, for example:
AppInfos = new List<AppInfo>
{
new AppInfo("Visual Studio", "A popular IDE."),
new AppInfo("Calculator", "Does the math for you."),
new AppInfo("Chrome", "A web browser.")
};
The tab control would bind its ItemsSource to the AppInfos.
<TabControl ItemsSource="{Binding AppInfos}">
In order to create a reusable visual representation, create a DataTemplate as ItemTemplate (this is the tab header) and a ContentTemplate (this is the tab content).
<TabControl ItemsSource="{Binding AppInfos}">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:AppInfo}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type local:AppInfo}">
<Grid>
<Label Content="{Binding Name}"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"
FontSize="18"/>
<Label Content="{Binding Description}"
HorizontalAlignment="Left"
Margin="11,44,0,0"
VerticalAlignment="Top"
FontSize="18"/>
<Button Content="Show Open Dialog"
IsEnabled="True"
HorizontalAlignment="Left"
Margin="11,185,0,0"
VerticalAlignment="Top" Width="194"
Command="{Binding DataContext.OpenCommand, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}"
CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Now, both templates are reused for each item in the AppInfos collection. The data template uses bindings to the corresponding properties, which automatically pick up the values for each item.
The Button is special here, since it can be clicked to execute an action. This action is probably the same for all items, except that it needs a concrete item to work with. You can encapsulate this logic in commands.
MVVM - Commands, RelayCommands and EventToCommand
This is another important aspect that can make your life easier, but is beyond the scope of this question. There are lots of tutorials out there. You could expose another property:
public ICommand OpenCommand{ get; }
The button in XAML binds its Command to this property (RelativeSource is needed to get the right data context for binding). The CommandParameter will pass the current AppInfo item to the command. In code, you could then execute your custom logic on this item. No need to know about user interface elements, only data.
Disclaimer: I know that this is pretty overwhelming. That is why I provided an immediate (dirty) solution to your issue and an example with links to resources to gradually learn a better way with concrete samples for your exact problem. Once you get used to MVVM, data binding and data templating, you will see how much easier more complex issues can be solved and what the benefits are in terms of maintainability and reuseability.
I have a ToolBar in my application which causes Problems.
I have "DropDown" Buttons inside the ToolBar (ToggleButton + Popup) Those DropDowns work properly if they are on the Visible Part of the ToolBar, they do not work properly if they are located in the ToolBarOverflowPanel.
If i Open a DropDown in the ToolBarOverflowPanel the Popup does not seem to receive focus. There are still hover effects (opposing to the behaviour of the same Popup in the visible toolbar which seems to consume all Mouse Events) and i can still click any other DropDown which opens the next Popup while the initial one stays open.
The following code is a full working sample to reproduce the behaviour.
<Window x:Class="ToolbarProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350"
Width="525">
<StackPanel>
<ToolBar ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate >
<StackPanel VerticalAlignment="Center"
Orientation="Horizontal">
<ToggleButton Name="ToggleButton"
ToolTip="ToolTip"
IsChecked="{Binding ElementName=ContextActionPopup, Path=IsOpen, Mode=TwoWay}"
ClickMode="Release">
<TextBlock Text="ICON"/>
</ToggleButton>
<Popup Name="ContextActionPopup"
StaysOpen="False">
<Border x:Name="Border"
Background="White"
Padding="1"
Visibility="Visible">
<TextBlock Text="Content" />
</Border>
</Popup>
</StackPanel>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
</StackPanel>
namespace ToolbarProblem
{
using System.Collections.Generic;
public partial class MainWindow
{
public MainWindow()
{
this.InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
public class MainWindowViewModel
{
public List<object> Items { get; } = new List<object>
{
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object()
};
}
}
I did tried the following approaches without any success:
Add some code to call Popup.Focus
Change ToggleButton.ClickMode to everything possible
Setting the Popup.StaysOpen property to true does seem to work, but is
of course inappropriate for my target
I am afraid that your XAML will never work properly. You can find the reason in the Popup control code. If you set the StaysOpen property to false, the Popup when opening, will call this private method (just use ILSpy to inspect):
private void EstablishPopupCapture()
{
if (!this._cacheValid[1] && this._popupRoot.Value != null && !this.StaysOpen && Mouse.Captured == null)
{
Mouse.Capture(this._popupRoot.Value, CaptureMode.SubTree);
this._cacheValid[1] = true;
}
}
So if there is no other control that is capturing mouse events (i.e. Mouse.Captured == null) your popup will capture them
to determine when one of these events occurs outside the Popup control.
as MSDN remarks. Please, note that Capture method and Captured properties are static, so just one control at a time can capture by using Mouse class.
Now just take a look to the Toolbar default style: you will find that its Popup control, named "OverflowPopup", has its StaysOpen property set to false.
So when you click on the overflow thumb, the "OverflowPopup" calls the EstablishPopupCapture while it is opening. In this case Mouse.Captured is null so it can capture mouse events.
After a while you click on a ToggleButton which is inside the "OverflowPopup" (so it will continue to stay open). Now your Popup - while opening - calls EstablishPopupCapture, but this time Mouse.Captured is not null. So it is not able to listen to mouse events by using Mouse class.
I guess you should consider changing your XAML or adopting a different solution (for example you can write your own template for your Toolbars in order to set OverflowPopup's StaysOpen property to true).
This is a simplified explanation, but the fact of the matter is that it is not possible to have two or more opened popups with StaysOpen set to false: they simply cannot work as expected. Just try to run this XAML:
<StackPanel Orientation="Horizontal">
<Button Content="I am button 1" Name="btn1" Height="20" />
<Button Content="I am button 2" Name="btn2" Height="20" />
<Popup StaysOpen="False" IsOpen="True" PlacementTarget="{Binding ElementName=btn1}">
<TextBlock Text="Popup1" Margin="6" Background="Red" />
</Popup>
<Popup StaysOpen="False" IsOpen="True" PlacementTarget="{Binding ElementName=btn2}">
<TextBlock Text="Popup2" Margin="6" Background="Red" />
</Popup>
</StackPanel>
And you will see that Popup2 will not work as it should do.
I have a ToolBar with a ItemsTemplate which works fine until the OverflowPanel is Available.
The OverflowPanel does not close if i select one of the context actions.
The Problem only occurs if the Items are added via the ItemsSource binding:
<ToolBar ItemsSource="{Binding ContextActionViewModels}"
Background="Transparent"
ToolBarTray.IsLocked="True"
FocusManager.IsFocusScope="False">
<ToolBar.ItemTemplateSelector>
<views:ContextActionTemplateSelector>
<views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<DataTemplate DataType="{x:Type viewModels:SimpleContextActionViewModel}">
<Button Name="Button"
Command="{Binding ActionCommand}"
Style="{StaticResource ToolBarButtonStyle}"
ToolTip="{userInterface:Translation Binding={Binding ToolTip}}">
<ContentControl Template="{Binding Icon,Converter={StaticResource NameToResourceConverter}}"
Margin="5"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Button>
</DataTemplate>
</views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<!-- Multiple DataTemplates follow!-->
Why is the DataTemplate / ItemTemplteSelector not working properly. While hardcoded Buttons in XAML work properly?
I uploaded a full sample that illustrates what is not working here:
Just Resize the window and try invoking one off the buttons in the OverflowPanel. While the 'ICommand' is executed properly the Popup stays open.
In the .NET framework source you can find the method that handles the closing behavior of OverflowPanel for ToolBar class:
private static void _OnClick(object e, RoutedEventArgs args)
{
ToolBar toolBar = (ToolBar)e;
ButtonBase bb = args.OriginalSource as ButtonBase;
if (toolBar.IsOverflowOpen && bb != null && bb.Parent == toolBar)
toolBar.Close();
}
When you define a DataTemplate and use ItemsSource property, the Parent property of the created button becomes null and the if check fails. This is the expected behavior of DataTemplate as described here:
For templates, the Parent of the template eventually will be null. To get past this point and extend into the logical tree where the template is actually applied, use TemplatedParent.
As a solution you can set the IsOverflowOpen property to false when you click any of the buttons:
<ToolBar Name="SomeToolBar" ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate DataType="local:ItemViewModel">
<Button Command="{Binding Command}" Content="{Binding Name}" Click="ButtonBase_OnClick"/>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SomeToolBar.IsOverflowOpen = false;
}
I have a XAML UserControl embedded in a WinForms/WPF Interop ElementHost control. The control is pretty simple - it's just a dropdown with a button - here's the entire markup:
<UserControl x:Class="Rubberduck.UI.FindSymbol.FindSymbolControl"
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:local="clr-namespace:Rubberduck.UI.FindSymbol"
mc:Ignorable="d"
d:DesignHeight="27" d:DesignWidth="270">
<UserControl.Resources>
<local:DeclarationImageConverter x:Key="DeclarationImageConverter" />
</UserControl.Resources>
<UserControl.CommandBindings>
<CommandBinding Command="local:FindSymbolControl.GoCommand"
Executed="CommandBinding_OnExecuted"
CanExecute="CommandBinding_OnCanExecute"/>
</UserControl.CommandBindings>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<ComboBox IsEditable="True"
ItemsSource="{Binding MatchResults}"
SelectedItem="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}"
IsTextSearchCaseSensitive="False"
IsTextSearchEnabled="True"
TextSearch.TextPath="IdentifierName">
<ComboBox.ItemTemplate>
<DataTemplate DataType="local:SearchResult">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="16" Width="16" Margin="2,0,2,0" Source="{Binding Declaration, Converter={StaticResource DeclarationImageConverter}}" />
<TextBlock Margin="2,0,2,0" Text="{Binding IdentifierName}" FontWeight="Bold" MinWidth="140" />
<TextBlock Margin="2,0,2,0" Text="{Binding Location}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Grid.Column="1"
Command="local:FindSymbolControl.GoCommand">
<Image Height="16" Source="pack://application:,,,/Rubberduck;component/Resources/arrow.png" />
</Button>
</Grid>
</UserControl>
The problem is that it doesn't work reliably, and far from instinctively.
If I type something in the box that actually matches an item, nothing happens until I manually select that item in the dropdown. Like here, I typed "sleepD", the box autocompleted to "sleepDelay", but the command is still disabled:
Once I've selected the item in the dropdown, the command button gets enabled as expected (although the image on the button doesn't show up grayed-out when the button is disabled, so it's not exactly as obvious as I intended it to be).
(the screenshot isn't really showing it, but there's only 1 match for that search)
If I click the button at that point, it works as expected. The problem is that if I make a new selection from the dropdown after that, the text box gets cleared instead of displaying the item I selected, and there's a weird delay during which the box is displaying what appears to be selected whitespace - this only seems to happen when the previous selection was made after selecting a value in the dropdown while the search text matches multiple entries, like "Sleep" above.
After the box got cleared, I can make a new selection from the dropdown and it will work as expected (except the VBE won't actually activate the CodePane I'm setting the selection to, but that's a separate issue).
The command implementation simply raises a Navigate event that passes a Declaration to the code that owns the VM instance.
The Search method, for which I need to add a .Take(50) after the .Select, to limit the number of returned results and perhaps reduce the lag a bit:
private void Search(string value)
{
var lower = value.ToLowerInvariant();
var results = _declarations.Where(
declaration => declaration.IdentifierName.ToLowerInvariant().Contains(lower))
.OrderBy(declaration => declaration.IdentifierName.ToLowerInvariant())
.Select(declaration => new SearchResult(declaration));
MatchResults = new ObservableCollection<SearchResult>(results);
}
private string _searchString;
public string SearchString
{
get { return _searchString; }
set
{
_searchString = value;
Search(value);
}
}
private SearchResult _selectedItem;
public SearchResult SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
}
}
private ObservableCollection<SearchResult> _matchResults;
public ObservableCollection<SearchResult> MatchResults
{
get { return _matchResults; }
set { _matchResults = value; OnPropertyChanged(); }
}
}
There's also an IValueConverter involved, that takes the Declaration in the SearchResult and switches on the declaration's DeclarationType enum to return a pack uri that points to the .png image to use in the dropdown list.
Aaah found it. It was all in the XAML.
Right here:
Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}"
That line doesn't belong there; binding the TextSearch.Text property instead...
TextSearch.Text="{Binding SearchString, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
Makes it all work as intended. No glitch, no lag. Well there is a lag when I first drop the dropdown, but that's another issue.
Lesson learned: when TextSearch is enabled on an editable combobox, don't bind the Text property, unless you want weird behavior.
I bind my wpf window to app layer class (WindowVM.cs) using DataContext in Window.xaml.cs constructor (DataContext = WindowVM). But, one control (btnAdd) I want to bind to Window.xaml.cs property. So in Window.xaml.cs constructor I add this.btnAdd.DataContext. This is Window.xaml.cs constructor and property to which I want bind Button btnAdd:
public Window()
{
InitializeComponent();
DataContext = WindowVM;
this.btnAdd.DataContext = this;
}
public RelayCommand Add
{
get
{
return _add == null ? _add= new RelayCommand(AddPP, CanAddPP) : _add;
}
set
{
OnPropertyChanged("Add");
}
}
Xaml looks like this (class PP is WindowVM property):
<TextBox Name="txtName" Text="{Binding PP.Name, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtSurname" Text="{Binding PP.Surname, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding Add}" Content="Add" ... />
And - everything works, but console output this:
BindingExpression path error: 'Add' property not found on 'object' ''WindowVM'...
In next calls there isn't any console output error for property Add.
Now I am a little bit confused because of this error. Is this error because of first DataContext (to WindowVM), because there isn't property Add, but with line this.btnAdd.DataContext property Add is found and it's the reason that it works?
Simply set the DataContext of the Button in the XAML using a RelativeSource:
<Button Command="{Binding Add}" Content="Add" DataContext="{Binding Add, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
I had this problem and I know this is an oldish post but I think this might help someone who stumbles on this in the future.
what I did was declare the viewmodels as resources
<Page.Resources>
<local:LocationListViewModel x:Key="LocationList" />
<local:LocationNewViewModel x:Key="NewLocation" />
<code:BinaryImageConverter x:Key="imgConverter" />
</Page.Resources>
then which ever control I wanted to be associated with said viewmodel I added this to their datacontext
<TabItem x:Name="tabSettingsLocations" x:Uid="tabSettingsLocations"
Header="Locations"
DataContext="{StaticResource ResourceKey=LocationList}">....
<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd"
Header="Add New"
DataContext="{StaticResource ResourceKey=NewLocation}">....
<Image x:Name="imgSettingsLocationMapNew" x:Uid="imgSettingsLocationMapNew"
Source="{Binding Map, Converter={StaticResource imgConverter},
Mode=TwoWay}"
DataContext="{StaticResource ResourceKey=NewLocation}" />
So in my example above I have Listview bound to the list viewmodel and I create a new single location for my new entry. You will notice that by creating it as a resource I can bind the tabitem and the image (which is not a child of the tab item) to the new location viewmodel.
My command for the addnew location is in the new location viewmodel.
<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd"
Header="Add New"
DataContext="{StaticResource ResourceKey=NewLocation}">....
<Button x:Name="btnSettingsLocationSaveAdd" x:Uid="btnSettingsLocationSaveAdd" Content="Submit" Margin="0,80,10,0"
VerticalAlignment="Top" Style="{DynamicResource ButtonStyle}" HorizontalAlignment="Right" Width="75"
Command="{Binding AddCommand}" />.....
Which is the child of the tabitem I bound to the new location viewmodel.
I hope that helps.
When you set the DataContext-Property, your Window resets the Bindings of it's child controls. Even the Binding of your button.
At this Point (before "button.DataContext = this" is evaluated) "Add" is searched in WindowVM. After this you set the Window class as buttons DC, and everything works fine.
To avoid the initial error, swap two lines from this
public Window()
{
InitializeComponent();
DataContext = WindowVM;
this.btnAdd.DataContext = this;
}
to this
public Window()
{
InitializeComponent();
this.btnAdd.DataContext = this;
DataContext = WindowVM;
}