I have class DesignerCanvas where I have RoutedCommand.
public class DesignerCanvas
{
public static RoutedCommand SelectAll = new RoutedCommand();
public DesignerCanvas()
{
this.CommandBindings.Add(new CommandBinding(DesignerCanvas.SelectAll, SelectAll_Executed));
SelectAll.InputGestures.Add(new KeyGesture(Key.A, ModifierKeys.Control));
}
}
In ResourcesDictionary I've got MyToolbar in this I've got buttons that have specify commands like this:
<Button Margin="3" Width="55" Style="{StaticResource ToolBarButtonBaseStyle}"
HorizontalContentAlignment="Center"
Command="{x:Static DesignerCanvas.SelectAll}"
CommandTarget="{Binding ElementName=MyDesigner} /">
And everything works perfect unless I don't close main window and than create it ones again with new MainWindow().Show(). All commands want to execute in old DesignerCanvas (from the original MainWindow) What can I do to change CommandTarget to new DesignerCanvas witch was created in new MainWindow?
EDIT
MyDesigner in MainWindow:
<s:DesignerCanvas
Focusable="true"
x:Name="MyDesigner"
Background="{StaticResource WindowBackgroundBrush}"
ContextMenu="{StaticResource DesignerCanvasContextMenu}"
SnapsToDevicePixels="True"
Margin="-2" />
Related
I am trying to make a contactlist with 2 different types of contacts, FysiekContactPersoon (Fysical persons) and WinkelOfBedrijf (Corporates). They both are inherited from the class ContactPersoon.
my MainWindow.xaml.cs
public partial class MainWindow : Window
{
ContactPersoonViewModel _viewmod = null;
public ContactPersoonViewModel ViewMod
{
get { _viewmod ??= new ContactPersoonViewModel(); return _viewmod; }
set => _viewmod = value;
}
public MainWindow()
{
InitializeComponent();
ViewMod.Import();
DataContext = ViewMod;
}
private void InfoButton_Click(object sender, RoutedEventArgs e)
{
DialogInfo dlg = new DialogInfo(ViewMod) { Owner = this };
if (dlg.ShowDialog() == true) { }
}
}
When the user selects a contact from a datagrid on mainwindow and presses the Info button, the dialog window opens.I have created two templates that normally have to be applied each to its corresponding class.But the dialogwindow is empty, despite the fact that the current item is shown properly in the viewModel when debugging.
My dialoginfo.xaml (simplified):
<ContentControl DataContext="{Binding CurrentCP}" Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="x:Type local:FysiekeContactpersoon">
<StackPanel Margin="5,5,5,5" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Content="Person:" HorizontalAlignment="Center" VerticalAlignment="Center" Width="114" Height="26" />
<TextBox x:Name="ContactNaam" HorizontalAlignment="Center" TextWrapping="Wrap" Text="{Binding Naam}" VerticalAlignment="Center" Width="218" Height="22"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="x:Type local:WinkelOfBedrijf">
<StackPanel Margin="5,5,5,5" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Content="Corporate:" HorizontalAlignment="Center" VerticalAlignment="Center" Width="114" Height="26" />
<TextBox x:Name="ContactNaam" HorizontalAlignment="Center" TextWrapping="Wrap" Text="{Binding Naam}" VerticalAlignment="Center" Width="218" Height="22"/>
</StackPanel>
</DataTemplate>
</ContentControl.Resources>
and my dialoginfo.xaml.cs
public partial class DialogInfo : Window
{
ContactPersoonViewModel _viewModel = null;
public ContactPersoonViewModel ViewModel { get => _viewModel; set => _viewModel = value; }
public DialogInfo(ContactPersoonViewModel vm)
{
ViewModel = vm;
InitializeComponent();
DataContext = vm.CurrentCP;
}
What am I doing wrong here? I was through a lot of similar threads, mostly pointing at this solution as correct and the simplest one, comparing with DataTemplateSelector or Property setters and triggers (which also aren't working with me- I've tried :().
Moreover, I have each second time a compilation fail "The key had already been added" of something, but the next compilation is perfectly succeeded after no code has been changed at all(WTF??!).Needless to say, how disappointed I am in XAML. I would appreciate some help in the form of a piece of a suitable code, or a very good tutorial link.
It looks like your code should basically work. The only problem I found is you type declaration on the DataTemplate.
For properties of type Type like Style.TargetType the XAML engine will convert the string representation of a type to an actual Type instance.
But this is not the case for properties like DataTemplate.DataType. Since DataTemplate defines the property DataType of type object, there will be no internal conversion from string to Type.
This is because DataTemplate.DataType expects a string for XML types and Type for objects.
Because you assigned a string to DataTemplate.DataType, no object type is resolved, as the data object is expected to be a XML object.
Using x:Type in order to define a Type rather than a string is correct, but you simply forgot to mark the declaration as markup extension using curly braces! Without this braces you are just defining a string value.
The correct syntax is:
<DataTemplate DataType="{x:Type local:FysiekeContactpersoon}">
...
</DataTemplate>
I am pretty new to WPF and MVVM so this may be a very easy question. I have an app with a button and a checkbox. Once the button is clicked it runs a command that then runs a script. The checkbox is an option to view an internet browser as the script runs. I am wondering how I can pass in wheather the checkbox is checked or not once the button is selected. I changed some of the coding names to be more basic. Here is my Xaml:
<StackPanel Margin="10">
<CheckBox Content="Option" IsChecked="True" />
<Button Height="20"
Content="Run Script"
Command="{Binding Script }"
/>
</StackPanel>
And here is the the ViewModel:
class MainWindowViewModel
{
public ICommand script{ get; set; }
public MainWindowViewModel()
{
script = new RelayCommand(o => MainButtonClick());
}
private void MainButtonClick()
{
Program start = new Program();
start.Begin();
}
}
You can bind the IsChecked of the CheckBox to a property in the ViewModel. Something like this should work:
<CheckBox Content="Option" IsChecked="{Binding ShowBrowser}" />
public bool ShowBrowser {get; set;}
You can then use the ShowBrowser property in your MainButtonClick method
Or you could use a Command Parameter as dymanoid pointed out in the comments. Like so:
<CheckBox Name="ShowBrowser" Content="Option" IsChecked="True" />
<Button Height="20"
Content="Run Script"
Command="{Binding Script }"
CommandParameter="{Binding ElementName=ShowBrowser, Path=IsChecked}
/>
And then your Method would look like this:
private void MainButtonClick(bool showBrowser)
{
Program start = new Program();
start.Begin();
}
This is of course assuming your RelayCommand class can handle parameters
I have a WPF project where I have created a UserControl for the purpose of making a custom ListViewItem which includes a close button inside. Here is the code for that:
<ListViewItem x:Name="lviTab" Height="36" Background="#232323"
MouseUp="LviTab_MouseUp" MouseEnter="LviTab_MouseEnter">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding TabText}" FontSize="15" />
<ListViewItem x:Name="lviTabClose" Margin="5, 0, 0, 0"
Padding="0, 0, 0, 0" MouseUp="LviTabClose_MouseUp">
<materialDesign:PackIcon Kind="Close" Foreground="White"
VerticalAlignment="Center"
HorizontalAlignment="Center" Width="20" Height="20" />
</ListViewItem>
</StackPanel>
</ListViewItem>
I am adjusting the text inside of each item by binding the text of the TextBlock inside of the UserControl to another class TabData.cs
public class TabData : UIElement
{
public TabData() : base()
{
}
public string TabText { get; set; }
}
In my ListView, I have set the DataTemplate to my UserControl. I have set the ListView ItemSource to a list of TabData objects.
tabs = new List<TabData>()
{
new TabData{TabText = "Tab 1"},
new TabData{TabText = "Tab 2"},
new TabData{TabText = "Tab 3"},
new TabData{TabText = "Tab 4"},
new TabData{TabText = "Tab 5"}
};
lvTabs.ItemsSource = tabs;
When moving the mouse over a ListViewItem, I need IsMouseOver to be true. I've tried inheriting UIElement to TabData but it hasn't worked. I'm quite new to WPF, I would appreciate any help with figuring out how I can retain IsMouseOver property when using a UserControl and setting the ItemSource to items which arn't ListViewItems.
Here's the simplest way to do what you're trying to do. When you populate a ListView with items from a collection, it creates its own ListViewItems. You don't need to create another ListViewItem inside each of its ListViewItems, much less a third one inside your own.
I'm going to dispense with the UserControl and put that XAML straight in a template. The reason for that is that we want the click handler to be in MainWindow.xaml.cs, where it can interact with the main viewmodel. We could fairly easily make that work with the UserControl in a couple of different ways, but I'm keeping this as simple as I can. Ideally you would use a Command for that, but that's one particular case where a little impurity in your MVVM won't ruin you. And for a case where the item UI is as simple as one textblock and one button, a UserControl is more than you need.
First, MainWindow.xaml
<Window.Resources>
<DataTemplate x:Key="TabListViewItemTemplate">
<DockPanel LastChildFill="False">
<TextBlock Text="{Binding TabText}" DockPanel.Dock="Left" />
<Button x:Name="TabCloseButton" Click="TabCloseButton_Click" DockPanel.Dock="Right" >
<Button.Template>
<ControlTemplate TargetType="Button">
<materialDesign:PackIcon
Kind="Close" Foreground="White"
VerticalAlignment="Center" HorizontalAlignment="Center"
Width="20" Height="20" />
</ControlTemplate>
</Button.Template>
</Button>
</DockPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListView
ItemsSource="{Binding TabItems}"
ItemTemplate="{StaticResource TabListViewItemTemplate}"
HorizontalContentAlignment="Stretch"
/>
</Grid>
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel
{
TabItems = {
new TabData { TabText = "Fred" },
new TabData { TabText = "Ginger" },
new TabData { TabText = "Herman" },
}
};
}
private void TabCloseButton_Click(object sender, RoutedEventArgs e)
{
if ((sender as FrameworkElement).DataContext is TabData tabData)
{
MessageBox.Show($"TabCloseButton_Click() {tabData.TabText}");
}
}
The TabData class. Don't inherit from UIElement. It's not an element in the user interface, it's just a plain C# class. If you were going to let the user edit TabText, you would make it a viewmodel that implements INotifyPropertyChanged.
public class TabData
{
public string TabText { get; set; }
}
The main viewmodel. Nothing we're doing here actually requires INotifyPropertyChanged on any of your classes, but you'll need it if you turn this into anything useful, so we'll include it.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public class MainViewModel : ViewModelBase
{
public ObservableCollection<TabData> TabItems { get; } = new ObservableCollection<TabData>();
}
I've got a WPF TextBox with TwoWay binding to a ViewModel property. I also have a ToolBar with a Button. When the Button is clicked, it executes a command on the same ViewModel that will do something with the property the TextBox is bound to.
Unfortunately it looks like the Binding only sends the text back to the binding target when the TextBox loses focus. The Button on the Toolbar however does not take focus when clicked. The upshot being that when the Command executes it does not have the text from the textbox, but rather the last value that was bound.
The Xaml looks like so:
<DockPanel LastChildFill="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<ToolBarTray Background="White" DockPanel.Dock="Top">
<ToolBar Band="1" BandIndex="1">
<Button Command="{Binding QueryCommand}">
<Image Source="images\media_play_green.png" />
</Button>
</ToolBar>
</ToolBarTray>
<DataGrid VerticalAlignment="Top" DockPanel.Dock="Top" Height="450" AutoGenerateColumns="True"
ItemsSource="{Binding}" DataContext="{Binding Results}" DataContextChanged="DataGrid_DataContextChanged"/>
<TextBox DockPanel.Dock="Bottom" Text="{Binding Sql, Mode=TwoWay}"
AcceptsReturn="True" AcceptsTab="True" AutoWordSelection="True" TextWrapping="WrapWithOverflow"/>
</DockPanel>
How do I get the TextBox's Text binding to update the ViewModel when the ToolBar button is pressed. There is nothing fancy going on in the ViewModel which looks like so:
public class MainViewModel : ViewModelBase
{
private readonly IMusicDatabase _database;
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IMusicDatabase database)
{
_database = database;
QueryCommand = new RelayCommand(Query);
}
public RelayCommand QueryCommand { get; private set; }
private async Task QueryAndSetResults()
{
Results = await _database.Query(Sql);
}
private void Query()
{
QueryAndSetResults();
}
private IEnumerable<object> _results;
public IEnumerable<object> Results
{
get
{
return _results;
}
private set
{
Set<IEnumerable<object>>("Results", ref _results, value);
}
}
private string _sql = "SELECT * FROM this WHERE JoinedComposers = 'Traditional'";
public string Sql
{
get { return _sql; }
set
{
Set<string>("Sql", ref _sql, value);
}
}
}
You can use the UpdateSourceTrigger property of the binding, setting it to PropertyChanged makes the TextBox refresh the binding every time the text changes, not just when losing focus:
<TextBox DockPanel.Dock="Bottom"
Text="{Binding Sql, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AcceptsReturn="True"
AcceptsTab="True"
AutoWordSelection="True"
TextWrapping="WrapWithOverflow"/>
More info at MSDN.
I have an issue with switching views in a WPF MVVM app.
When clicking on menu items defined in the main view, switching works fine.
When clicking on a button in a child view, switching does not work as expected.
If I set contentcontrol in child view (where the button is) as well as parent view,
the child view gets displayed mixed with previous displayed view, a button from one view and background from the one I want to switch to.
Without it, the debugger shows something happening, similar steps in the ViewModelBase class to what happened when choosing from the menu mentioned above but no visual changes in the window.
I have commands in a ViewmodelBase (that all viewmodels inherit from either directly or through a mainviewmodel) class that gets called from bindings such as in the XAML above.
CurrentViewModel is a property in ViewModelBase that is used to determine which view gets displayed. In the constructor of ViewModelBase i set commands for example:
CategoryVMCommand = new RelayCommand(() => ExecuteCategoryVMCommand());
(RelayCommand from the line above comes from the MVVM light framework,
although its not necessary for the solution to use that framework)
I found many tutorials and answers for similar problems, but couldnt get any of them to work. For example I tried, without success, using IOC for a similar problem in the below link:
MVVM Main window control bind from child user control
Here are some of the code involved and description of what Im doing:
Main Window:
<Grid>
<ContentControl Content="{Binding CurrentViewModel}" />
<DockPanel Margin="0,0,0,50">
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="_Open" Command="{Binding CategoryVMCommand}"/>
<MenuItem Header="_Close"/>
<MenuItem Header="_Save"/>
</MenuItem>
<MenuItem Header="_New">
<MenuItem Header="_Create" Command="{Binding MainControlVMCommand}"/>
</MenuItem>
</Menu>
<StackPanel></StackPanel>
</DockPanel>
</Grid>
</Window>
Then I select Menu item New, the following view is displayed:
<UserControl x:Class="WpfApplication1.MainControl"
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"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<!--<ContentControl Content="{Binding CurrentViewModel, Mode=OneWay}" />-->
<TextBlock HorizontalAlignment="Left" Margin="10,20,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="39" Width="144" FontSize="24"><Run Language="en-gb" Text="TITLE"/><LineBreak/><Run Language="en-gb"/></TextBlock>
<Button Content="Open category" HorizontalAlignment="Left" Margin="10,136,0,0" VerticalAlignment="Top" Width="153" Height="63" Command="{Binding CategoryVMCommand}" />
<Button Content="Create new category" HorizontalAlignment="Left" Margin="10,218,0,0" VerticalAlignment="Top" Width="153" Height="63"/>
<ListBox HorizontalAlignment="Left" Height="145" Margin="293,136,0,0" VerticalAlignment="Top" Width="201" Background="#FFDDDDDD"/>
<TextBlock HorizontalAlignment="Left" Margin="293,107,0,0" TextWrapping="Wrap" Text="Recently Used" VerticalAlignment="Top" FontSize="18"/>
</Grid>
</UserControl>
button open category clicked, and Currentviewmodel set code executes (depending on ContenControl in MainControl view being commented out or not either
return or assigned), then The ExecuteCategoryCommand get executed. Then the line with the expected command in ViewModelBase constructor executes, although
either no change or the mixed result i mentioned originally
ViewModelBase class:
namespace ViewModel
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ViewModelBase _currentViewModel;
public ICommand CategoryVMCommand { get; private set; }
public ICommand MainControlVMCommand { get; private set; }
protected void NotifyPropertyChanged( String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public ViewModelBase()
{
MainControlVMCommand = new RelayCommand(() => ExecuteMainControlVMCommand());
CategoryVMCommand = new RelayCommand(() => ExecuteCategoryVMCommand());
}
public ViewModelBase CurrentViewModel
{
get
{
return _currentViewModel;
}
set
{
if (_currentViewModel == value)
return;
_currentViewModel = value;
NotifyPropertyChanged("CurrentViewModel");
}
}
protected void ExecuteCategoryVMCommand()
{
CurrentViewModel = null;
CurrentViewModel = new CategoryVM();
}
protected void ExecuteMainControlVMCommand()
{
CurrentViewModel = null;
CurrentViewModel = new MainControlVM();
}
}
}
So my question is how can I click the button in the child view, send command from ViewModelBase, set CurrentViewModel, and successfully switch views within one window without any visual remains of the previously displayed view?
Thanks for any help.