I am having a problem hooking up some events in my custom dropdown or Popup template in my combobox.
This custom template by the way looks like the one in IE 10. Here's the picture
I got the partial look (the one below is mine). But I'm having a problem deleting an item in history list. Below is my PART_Popup template
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MinWidth="{Binding ActualWidth, ElementName=Placement}">
<Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<!--<ScrollViewer>-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel>
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Border Padding="5">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="History" Foreground="{StaticResource SeparatorLine}" />
<Border Height="2" Grid.Column="1" Margin="5,0,0,0" BorderBrush="{StaticResource SeparatorLine}" BorderThickness="0.5" />
</Grid>
<ListBox x:Name="listHistory" BorderThickness="0" Margin="0" Padding="0" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding }" />
<Button Grid.Column="1" HorizontalAlignment="Right" x:Name="btnDeleteHistoryItem" Content="r" FontFamily="Marlett" Style="{DynamicResource ButtonStyle}" Visibility="Hidden" Opacity="0.75" />
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Visibility" TargetName="btnDeleteHistoryItem" Value="Visible" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
</StackPanel>
<Border Grid.Row="1" BorderBrush="{StaticResource SeparatorLine}" BorderThickness="0,1,0,0" Padding="5" Height="30">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#33000000" Offset="0"/>
<GradientStop Offset="1"/>
<GradientStop Offset="0.375"/>
</LinearGradientBrush>
</Border.Background>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="btnClearHistory" Content=" Clear History " Style="{DynamicResource ButtonStyle}" />
</StackPanel>
</Border>
</Grid>
<!--</ScrollViewer>-->
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
alright, in the Listbox(listHistory) I have a button there called btnDeleteHistoryItem and I cannot hook it. It's returning a Null error in my code here
protected override void OnSourceInitialized(EventArgs e)
{
ListBox lb = (ListBox)cbSearch.Template.FindName("listHistory", cbSearch);
lb.ItemsSource = this.SearchHistory;
lb.SelectionChanged += cbResults_SelectionChanged;
Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb);
// if (btnDeleteHistoryItem != null)
{
btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
}
// or --------------
Button btnDeleteHistoryItem = (Button)cbSearch.Template.FindName("btnDeleteHistoryItem", cbSearch);
// if (btnDeleteHistoryItem != null)
{
btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
}
}
Coding is a little classic here, so no MVVM implemented in hooking with events. So the problem is this Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb); or Button btnDeleteHistoryItem = (Button)cbSearch.Template.FindName("btnDeleteHistoryItem", cbSearch);
line where it cannot find that btnDeleteHistoryItem.
How can I resolve this?
-- UPDATE --
lb.Loaded += (a, b) =>
{
Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb);
//if (btnDeleteHistoryItem != null)
{
btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
}
};
doesn't work either
Using a command will probably be the easiest solution here. You can hook a command up to your delete buttons and pass the current item in as the parameter. Here's a simple sample to illustrate.
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
SomeCommand = new MySampleCommand();
cbo.Items.Add("Hello");
cbo.Items.Add("Item 1");
cbo.Items.Add("Another Item");
cbo.Items.Add("Something else");
cbo.Items.Add("Yet another item");
}
public MySampleCommand SomeCommand { get; set; }
}
public class MySampleCommand : ICommand {
public bool CanExecute(object parameter) {
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) {
MessageBox.Show(string.Format("You are trying to remove {0}", parameter.ToString()));
}
}
Your combobox binding would look something like:
<Window x:Class="SOComboWithEmbeddedButton2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="ListBoxItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Button Command="{Binding SomeCommand, ElementName=Window}" CommandParameter="{Binding}" Content="Remove" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ComboBox ItemTemplate="{StaticResource ListBoxItemTemplate}" x:Name="cbo" />
</StackPanel>
Just adjust the Command binding on the DataTemplate to point to the appropriate object that has an instance of your command object. Hope that helps!
Related
I have got a problem with my WPF MVVM application.
I have got a view (CaisseView.xaml) and its ViewModel (CaisseViewModel.cs which is used as Datacontext of the View called since another .cs).
Into my view i am using an itemsControl in order to have a button for each element of a observablecollection of a Model. Moreover into my view I have got an ListView. I want that every time a button of the itemsControl is clicked a new line appears into my Lisview.
This last thing is what it does not work!!
Using debug I enter into my command, the command add an object into my observable collection but the app does not actualize the View.
Into my ViewModel, I am using INotifyPropertyChanged, RelayCommand and ICommand.
I let you my code.
Can you help me with my matter ???
thanks:
My View :
<UserControl x:Class="LMarket.Views.CaisseView"
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:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:LMarket.Views"
xmlns:localM="clr-namespace:LMarket.Models"
xmlns:localModel="clr-namespace:LMarket.ViewModels"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d">
<UserControl.Resources>
<localModel:CaisseViewModel x:Key="CaisseViewModel"/>
<localM:ProduitDuStock x:Key="ProduitDuStockModel"/>
</UserControl.Resources>
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image Cursor="Hand" Source="/Resources/homepage.png" Width="32" Height="32" HorizontalAlignment="Left" Margin="15,10" Grid.Row="0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding DataContext.OnHomeCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
<Image Cursor="Hand" Source="/Resources/LogOut.png" Width="32" Height="32" HorizontalAlignment="Right" Margin="15,10" Grid.Row="0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding DataContext.BackConnectionCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderThickness="2" BorderBrush="Azure" Margin="5,5,1,5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="1*" />
<RowDefinition Height="50" />
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#FFC2E0ED">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Image Cursor="Hand" VerticalAlignment="Center" Width="32" Height="32" Source="/Resources/files-and-folders.png"/>
<Image Cursor="Hand" VerticalAlignment="Center" Margin="10,0" Width="32" Height="32" Source="/Resources/cancel.png"/>
</StackPanel>
</Grid>
<ListView Grid.Row="1" Margin="5" SelectionMode="Single"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Path=LstProducts, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate DataType="localM:ProduitDuStock">
<StackPanel Margin="2" MinWidth="244">
<StackPanel.Resources>
<Style TargetType="{x:Type StackPanel}">
<!-- <Style.Triggers>
<DataTrigger Binding="{Binding Path=Selected}" Value="True">
<Setter Property="Background" Value="LightBlue"/>
</DataTrigger>
</Style.Triggers> -->
</Style>
</StackPanel.Resources>
<DockPanel >
<TextBlock FontWeight="Bold" Text="Produit: " DockPanel.Dock="Left" Margin="5,0,5,0"/>
<TextBlock Text=" " />
<TextBlock Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Foreground="Green" FontWeight="Bold" />
</DockPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<DockPanel Grid.Row="2" Margin="5" Background="#FF296B88">
<Label Content="TOTAL" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="14" FontWeight="Bold" FontFamily="Rockwell Condensed" Foreground="#FFF2FF5A" />
<Label Content="$$" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="14" FontWeight="Bold" FontFamily="Rockwell Condensed" Foreground="#FFF2FF5A" />
</DockPanel>
<Grid Grid.Row="3" Background="#FFC2E0ED">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="2">
<Button Cursor="Hand" Width="80" Content="Cuenta %" Background="#FF98ADA8" BorderBrush="#FF4A6DFF" Margin="2,0">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="8"/>
</Style>
</Button.Resources>
</Button>
<Button Cursor="Hand" Width="80" Content="Producto %" Background="#FF98ADA8" BorderBrush="#FF4A6DFF" Margin="2,0">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="8"/>
</Style>
</Button.Resources>
</Button>
<Button Cursor="Hand" Width="80" Content="-1" Background="#FFDDCF00" BorderBrush="#FF4A6DFF" FontWeight="Bold" FontSize="20">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="8"/>
</Style>
</Button.Resources>
</Button>
<Button Cursor="Hand" Width="80" Content="+1" Background="#FF79C837" BorderBrush="#FF4A6DFF" FontWeight="Bold" FontSize="20" Margin="2,0">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="8"/>
</Style>
</Button.Resources>
</Button>
<Button Cursor="Hand" Width="auto" Content=" Validar para Pagar " Command="{Binding AddNewProductCommand}" Background="#FF98ADA8" BorderBrush="#FF4A6DFF" FontSize="19" Margin="80,0,2,0" Foreground="Black">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="8"/>
</Style>
</Button.Resources>
</Button>
</StackPanel>
</Grid>
</Grid>
</Border>
<Border Grid.Column="1" BorderThickness="2" BorderBrush="Azure" Margin="1,5,5,5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" MaxHeight="720"/>
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Background="Transparent"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding ListOfProducts}" MaxWidth="762">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Cursor="Hand" CornerRadius="8" Margin="4,3" BorderThickness="1.5" BorderBrush="#FFFDFF00">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF6F92DC" Offset="1"/>
<GradientStop Color="#FFA6DBFF" Offset="0.534"/>
</LinearGradientBrush>
</Border.Background>
<Button BorderBrush="Transparent" Background="Transparent" Command="{Binding Source={StaticResource CaisseViewModel}, Path=AddNewProductCommand}" CommandParameter="{Binding Id}">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="8"/>
</Style>
</Button.Resources>
<StackPanel Orientation="Vertical" Margin="2" Width="80" Height="80">
<Image Source="{Binding ProductImageUrl}" Width="40" Height="40" Margin="0,10,0,5"
HorizontalAlignment="Center"/>
<Label Content="{Binding Surname}" FontWeight="Bold" HorizontalAlignment="Center" FontSize="11"/>
</StackPanel>
</Button>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Border>
</Grid>
</Grid>
</UserControl>
And my ViewModel:
using LMarket.Models;
using LMarket.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml;
namespace LMarket.ViewModels
{
class CaisseViewModel : INotifyPropertyChanged
{
//Event for INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string str = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(str));
}
#region Attributs
private ObservableCollection<ProduitDuStock> listOfProducts;
private ObservableCollection<ProduitDuStock> lstProducts;
private ProduitDuStock selectedProduct;
#endregion
#region Getter/Setter
public ObservableCollection<ProduitDuStock> ListOfProducts
{
get
{
return listOfProducts;
}
set
{
if (listOfProducts != value)
{
listOfProducts = value;
}
NotifyPropertyChanged();
}
}
public ObservableCollection<ProduitDuStock> LstProducts
{
get
{
return lstProducts;
}
set
{
if (lstProducts != value)
{
lstProducts = value;
}
NotifyPropertyChanged();
}
}
public ProduitDuStock SelectedProduct
{
get
{
return selectedProduct;
}
set
{
if (selectedProduct != value)
{
selectedProduct = value;
NotifyPropertyChanged();
}
}
}
#endregion
#region Constructeur
public CaisseViewModel()
{
ListOfProducts = new ObservableCollection<ProduitDuStock>();
LstProducts = new ObservableCollection<ProduitDuStock>();
toto();
//Lecture du fichier ou son enregistrer les données du stock (En local sur un .xml)
try
{
//Ouverture du document XML
XmlDocument xmlDoc = new XmlDocument();
string Path = Directory.GetCurrentDirectory().ToString();
Path = Path + "\\Configuration\\Stockes.xml";
xmlDoc.Load(Path);
//Recupérer la section des produits
XmlNodeList nodeList = xmlDoc.DocumentElement.SelectNodes("/Stockes/Products/product");
//Pour chaque produit dans le fichier, créer un objet ProduitDuStock et l'ajouter à l'ObservableCollection.
foreach (XmlNode node in nodeList)
{
//Creation du nouvel objet
ProduitDuStock testProduit = new ProduitDuStock();
foreach (XmlAttribute att in node.Attributes)
{
//Récupération des parametres de l'objet.
switch (att.Name)
{
case "id":
testProduit.Id = int.Parse(att.Value);
break;
case "name":
testProduit.Name = att.Value;
break;
case "pays":
testProduit.Pays = att.Value;
break;
case "surname":
testProduit.Surname = att.Value;
break;
case "quantite":
testProduit.Quantite = int.Parse(att.Value);
break;
case "unitprice":
testProduit.UnitPrice = int.Parse(att.Value);
break;
case "productImageUrl":
testProduit.ProductImageUrl = att.Value;
break;
default:
break;
}
}
//Ajouter l'objet créé a notre ObservableCollection.
ListOfProducts.Add(testProduit);
}
}
catch (Exception ex)
{
}
}
#endregion
#region Command Definition
//Event RelayCommand Definition
private RelayCommand addProductToBill = null;
public ICommand AddProductToBill
{
get
{
if (addProductToBill == null)
{
addProductToBill = new RelayCommand(OnAddProductToBill);
}
return addProductToBill;
}
}
private RelayCommand addNewProductCommand = null;
public ICommand AddNewProductCommand
{
get
{
if (addNewProductCommand == null)
{
addNewProductCommand = new RelayCommand(OnAddNewProductCommand);
}
return addNewProductCommand;
}
}
#endregion
#region Command function definition
//Event lorsque le curseur rentre dans la zone du boutton "Stock"
private void OnAddNewProductCommand(object obj)
{
toto();
}
private void OnAddProductToBill(object obj)
{
toto();
}
#endregion
#region function
public void toto()
{
ProduitDuStock myAddProductToBill = new ProduitDuStock();
myAddProductToBill.Name = "trrt";
LstProducts.Add(myAddProductToBill);
}
#endregion
}
}
the important part into the view is the next:
<Button BorderBrush="Transparent" Background="Transparent" Command="{Binding Source={StaticResource CaisseViewModel}, Path=AddNewProductCommand}" CommandParameter="{Binding Id}">
remember that this button is defined into a itemsControl ! So the Datacontextof the button is not my viewModel that why I am using a staticResource...
I hope something will help me :) thank you very much.
Welcome to SO! For future reference, you'll have a much better chance of getting your questions answered if you post an MCVE, there's enough unnecessary code in what you posted to put most people here off.
To answer your question, there are actually many things wrong with this code, but the main issue is that you're creating callback handlers of type RelayCommand, yet your command handler (AddProductToBill) expects an object parameter and should thus be of type RelayCommand<object>. (More confusingly, one of your bindings is passing in CommandParameter, while the other is not).
Another thing to avoid is declaring your view model in your resources block, this is generally not done for a variety of reasons (are you sure you're assigning that instance as your DataContext and not declaring a new one?). If you're assigning your DataContext elsewhere then your list elements can bind to it with something like this:
<UserControl ...
x:Name="_this">
.
.
<!-- Inside ItemsControl.ItemTemplate -->
<Button
Command="{Binding ElementName=_this, Path=DataContext.MyHandler}"
CommandParameter="{Binding}" ... />
I have a ListViewItem which has an Eventhandler attatched and inside the ControlTemplate of the ListViewItem is a Button which does something different. But if I click the Button, the Behaviour is like I clicked on the ListViewItem.
AppointmentOverview.xaml:
<Window.Resources>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource NormalListViewItem}">
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="ListViewItem_PreviewMouseLeftButtonDown" />
</Style>
</Window.Resources>
Styles.xaml(My ResourceDictionary):
<!--Style für die normalen ListViewItems-->
<Style x:Key="NormalListViewItem" TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="#5076A7" BorderThickness="1">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFFFFF" Offset="0.0"/>
<GradientStop Color="#FFFEB603" Offset="1.0"/>
</LinearGradientBrush>
</Border.Background>
<StackPanel TextElement.FontFamily="Segoe UI" TextElement.FontSize="12">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="15"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="Betreff" Padding="3,0,0,0" Text="{Binding Betreff}" TextTrimming="CharacterEllipsis" Grid.Column="0" Grid.Row="0"/>
<Button Grid.Column="1" Grid.Row="0" Style="{StaticResource ListViewItemButton}"/>
AppointmentOverview.xaml.cs:
private void ListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var item = sender as ListViewItem;
if (item != null)
{
AppointmentOverviewViewModel apvm = this.DataContext as AppointmentOverviewViewModel;
apvm.editAppointment(item);
}
}
It worked when I had the complete ListViewItem Style in the Window.Resources of Appointmentoverview.xaml. But I didn't like that because the would kind of beat the purpose of Styles.xaml. Also I didn't have a Style for the Button, just did all the Styling inside the Button. But this was very Basic and now I need more complex Styling so I wanted to create a separate Style.
<Button FontSize="7" Content="X" Grid.Column="1" Grid.Row="0"
Command="{Binding DataContext.DeleteButtonCommand, RelativeSource={
RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding ItemId}"/>
Update:
If I observe my EventHandler this is also triggered if the Button is pressed. It just says the source is a Button but the command is not executed.
Styles.xaml.cs
void ListViewItem_MouseLeftDown(object sender, MouseButtonEventArgs e)
{
DependencyObject current = sender as DependencyObject;
while (current != null && current.GetType() != typeof(ListViewItem))
{
current = VisualTreeHelper.GetParent(current);
}
var item = current as ListViewItem;
if (item != null)
{
Window parent = Window.GetWindow(current);
AppointmentOverviewViewModel apovm = parent.DataContext as AppointmentOverviewViewModel;
apovm.editAppointment(item);
}
}
Styles.xaml
<!--DataTemplate für die normalen ListViewItems-->
<DataTemplate DataType="{x:Type local:SCSMAppointment}">
<Border BorderBrush="#5076A7" BorderThickness="1" PreviewMouseLeftButtonDown="ListViewItem_MouseLeftDown">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFFFFF" Offset="0.0"/>
<GradientStop Color="#FFFEB603" Offset="1.0"/>
</LinearGradientBrush>
</Border.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<Button FontSize="7" Content="X" DockPanel.Dock="Right" Width="15"
Command="{Binding DataContext.DeleteButtonCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding ItemId}"/>
<TextBlock Name="Betreff" Padding="3,0,0,0" Text="{Binding Betreff}" TextTrimming="CharacterEllipsis" />
</DockPanel>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<TextBlock Padding="3,0,0,0" Text="{Binding Kunde}"/>
<TextBlock Padding="3,0,0,0" Text="|"/>
<TextBlock Padding="3,0,0,0" Text="{Binding IncidentId}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Row="2">
<TextBlock FontWeight="Bold" Padding="3,0,0,0" Text="{Binding Ort}"/>
<TextBlock Padding="3,0,0,0" Text="("/>
<TextBlock Text="{Binding Alternative}"/>
<TextBlock Text=")"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
It is apparent that your XAML is not correct. A ControlTemplate is primarily used to Specify the visual structure and behavioral aspects of a Control that can be shared across multiple instances of the control. In plain English, that means that they are used to change the default look of a Control. If that is not what you are doing, then you should not be using them.
On the other hand, DataTemplates Describe the visual structure of a data object, so you should be declaring DataTemplates to define what your data looks like instead of using ControlTemplates. Therefore, I suggest that you read the Data Binding Overview page on MSDN, which will give you a much better understanding, before you continue to work with WPF.
When you have updated your project, your problem will probably fix itself, but if it doesn't please return here to edit your question with your new XAML.
I'm trying to acomplish exactly what this question is requesting, but unfortunately the code sample that was provided as the answer is gone, and I'm also not using WPF Toolkit, here the question that he did:
I am trying to Merge cells in WPF toolkit datagrid .I am trying to do something as shown in the image below.We can do this in Winforms datagrid.But how to do this using WPF toolkit datagrid ?.Or is there any alternative controls..?
Can we do this using listview or listbox..? Or is there any free
controls available which have this functionality ?
I found several answers that manage to do this with the DataGridView control, but I do not want to use Form objects in a WPF project, is there a way to acomplish this?
Recource
<Window.Resources>
<Color x:Key="customBlue" A="255" R="54" G="95" B="177" />
<SolidColorBrush x:Key="customBlueBrush" Color="{StaticResource customBlue}"></SolidColorBrush>
<SolidColorBrush x:Key="customBlueBrushOpacity" Color="LightGray" Opacity="0.11"></SolidColorBrush>
<Style x:Key="calcyListbox" TargetType="ListBox">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="35"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Height="30" VerticalAlignment="Top" Background="{StaticResource customBlueBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Manufacturer" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" ></TextBlock>
<TextBlock Text="Name" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1"></TextBlock>
<TextBlock Text="CPU" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2"></TextBlock>
<TextBlock Text="RAM" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="3"></TextBlock>
<TextBlock Text="Price" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="4"></TextBlock>
</Grid>
<Border Grid.Row="1" SnapsToDevicePixels="True" Background="Transparent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0">
<ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="0">
<ItemsPresenter />
</ScrollViewer>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="noStyleToListboxItem" TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border>
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
xaml
<ListBox MaxHeight="300" ItemsSource="{Binding ManufacturerList}" Background="{StaticResource customBlueBrushOpacity}" x:Name="ManufacturerListBox" ScrollViewer.VerticalScrollBarVisibility="Auto" Style="{StaticResource calcyListbox}" ItemContainerStyle="{StaticResource noStyleToListboxItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Company}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Border BorderThickness="0,0,0,1" BorderBrush="Black" ></Border>
<ListBox Grid.Column="1" BorderThickness="1,0,1,1" Background="{StaticResource customBlueBrushOpacity}" HorizontalContentAlignment="Stretch" ItemsSource="{Binding Models}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="0"></Border>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="1"></Border>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="2"></Border>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0"/>
<TextBlock Text="{Binding CPU}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1"/>
<TextBlock Text="{Binding Ram}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2"/>
<TextBlock Text="{Binding price}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="3"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
c#
InitializeComponent();
List<Manufacturer> ManufacturerList = new List<Manufacturer>();
ManufacturerList.Add(new Manufacturer()
{
Company = "DEll",
Models = new List<Model>(){new Model(){CPU = "T7250", Name = "Inspiron1525", price =234434 , Ram= "2048 MB" },
new Model(){CPU = "T5750", Name = "Studio 1535", price =234443 , Ram= "2048 MB" },
new Model(){CPU = "T5780", Name = "Vastro 1510", price =234434 , Ram= "2048 MB" },}
});
ManufacturerList.Add(new Manufacturer()
{
Company = "Lenovo",
Models = new List<Model>(){new Model(){CPU = "T1230", Name = "l123", price =23546454 , Ram= "1024 MB" },
new Model(){CPU = "T1230", Name = "l1423", price =2346456 , Ram= "1024 MB" },
new Model(){CPU = "T1230", Name = "ldf123", price =2344646 , Ram= "1024 MB" },}
});
ManufacturerListBox.ItemsSource = ManufacturerList;
public class Manufacturer
{
public string Company { get; set; }
public List<Model> Models { get; set; }
}
public class Model
{
public string Name { get; set; }
public string Ram { get; set; }
public double price { get; set; }
public string CPU { get; set; }
}
For a merged cell, bind its Margin and set it to a negative value equal to the sum width/height of the associated merged cells - then the cell will spill across and on top the neighboring cells.
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="{{Binding BorderThickness[5], Mode=OneWay}}"/>
<Setter Property="Margin" Value="{{Binding CellMargins[5], Mode=OneWay}}"/>
<Setter Property="Block.TextAlignment" Value="Center"/>
</Style>
The bindings are indexed because CellMargins is an ObservableCollection of Thickness. The viewmodel is row-level, so each column binds to a different index.
Before this gets downvoted, let me say this can lead to some undesirable visual quirks. Getting this to work perfectly requires a bit of code-behind, such as:
The cell margins' negative values need to be updated whenever a column width changes, so I handled the datagrid's LayoutUpdated event, where I iterated through the columns, measured their current ActualWidths and updated the margins accordingly. I likewise update the BorderThickness to show/hide cell borders as needed. Disabling the selection highlight for cells that are "hidden" is another trick.
Note this means keeping track of what cells (i.e., which row viewmodels and column indices) you want merged separately in code-behind.
This method is complicated and probably not the easiest for every situation, but I found it useful for my purposes. I needed a Datagrid where the user could merge/unmerge cells and create/delete columns, similar to Excel. Furthermore, I wanted to stick with DataGridTextColumns (instead of custom TemplateColums) because of their built-in functionality (recognized keyboard edit commands, copy/paste, etc.)
I'm trying to create a tab control in which the Header on the TabItem is binded in a textbox that is in the controltemplate of the tabcontrol. how can i do this through binding in the style?
Here's my code:
<Style x:Key="TabControlTest1" TargetType="TabControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!--Area for TabItems-->
<Border Grid.Column="0"
Grid.Row="0"
>
<TabPanel IsItemsHost="True"
x:Name="HeaderPanel"
Background="Transparent" />
</Border>
<!--Content of SelectedItems-->
<Border Grid.Column="1"
BorderBrush="{DynamicResource TabControlContentPresentBorderBrush}"
BorderThickness="0,1,1,1"
Background="{DynamicResource TabControlContentPresentBackgroundBrush}"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--///This is Where I want to bind the Header///-->
<Label
Grid.Row="0"
Foreground="AliceBlue"
Content="{Binding Header, ElementName=TabItem}"/>
<ContentPresenter
Grid.Row="1"
x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
Margin="5"/>
</Grid>
</Border>
<ControlTemplate x:Key="TabItemControlTemplate" TargetType="{x:Type TabItem}">
<!--Grid Defines Height and also hold content header-->
<Grid>
<Border Background="{DynamicResource TabItemContentPresentBackgroundBrush}"
Margin="0,0,0,5">
<!--Content of TabItem will be rendered-->
<ContentPresenter
x:Name="ContentSite"
Margin="3"
HorizontalAlignment="Left"
VerticalAlignment="Center"
RecognizesAccessKey="True"
ContentSource="Header"/>
</Border>
</Grid>
</ControlTemplate>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
try this
<Label Grid.Row="0"
Foreground="AliceBlue"
Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabControl}
, Path=SelectedItem.Header}"/>
You could try and achieve this either using TemplateBinding or using RelativeSource.
TemplateBinding:
<Label Grid.Row="0"
Foreground="AliceBlue"
Content="{TemplateBinding Header}"/>
RelativeSource:
<Label Grid.Row="0"
Foreground="AliceBlue"
Content="{Binding Path=Header, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabItem}}"/>
This isn't using a control template, but demonstrates binding a textbox to the tab header through a view model. Note that I'm using MVVM light (ViewModelBase and Set()), but you can replace with your own INotifyPropertyChanged support if necessary.
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type Samples:TabBindingViewModel}">
<StackPanel>
<TextBlock Text="{Binding MyContent}"/>
<TextBox Text="{Binding Header}"/>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<TabControl
ContentTemplate="{StaticResource ContentTemplate}"
DisplayMemberPath="Header"
ItemsSource="{Binding Items}" />
</Grid>
public class TabContentToHeaderViewModels : ViewModelBase
{
private readonly ObservableCollection<TabContentToHeaderViewModel> _items;
public TabContentToHeaderViewModels()
{
_items = new ObservableCollection<TabContentToHeaderViewModel>
{
new TabContentToHeaderViewModel(1),
new TabContentToHeaderViewModel(2),
new TabContentToHeaderViewModel(3),
};
}
public IEnumerable<TabContentToHeaderViewModel> Items
{
get { return _items; }
}
}
public class TabContentToHeaderViewModel : ViewModelBase
{
public TabContentToHeaderViewModel() : this(0)
{
}
public TabContentToHeaderViewModel(int n)
{
Header = "I'm the header: " + n.ToString(CultureInfo.InvariantCulture);
MyContent = "I'm the content: " + n.ToString(CultureInfo.InvariantCulture);
}
private string _header;
public string Header
{
get { return _header; }
set { Set(() => Header, ref _header, value); }
}
public string MyContent { get; set; }
}
Currently I am setting the background on each Grid row individually:
<Grid>
<Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions>
<Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition /><ColumnDefinition /><ColumnDefinition /><ColumnDefinition /><ColumnDefinition /></Grid.ColumnDefinitions>
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="6" Height="24" BorderBrush="#FF252A30" CornerRadius="10" BorderThickness="1">
<Border.Background>
<LinearGradientBrush EndPoint="1.036,0.367" StartPoint="-0.194,0.362">
<GradientStop Color="#AAE098" Offset="0.1"/>
<GradientStop Color="#D5E9D4" Offset="0.9"/>
</LinearGradientBrush>
</Border.Background>
</Border>
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="6" Height="24" BorderBrush="#FF252A30" CornerRadius="10" BorderThickness="1">
<Border.Background>
<LinearGradientBrush EndPoint="1.036,0.367" StartPoint="-0.194,0.362">
<GradientStop Color="#AAE098" Offset="0.1"/>
<GradientStop Color="#D5E9D4" Offset="0.9"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
Surely there must be some way to set this Border once for all rows. How is that done?
Thanks!
Or you could use this grid I just made. It will automatically add a border to every cell in the grid. This is the result:
C#:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace GridWithBorder
{
public class BorderGrid : Grid
{
protected override void OnRender(DrawingContext dc)
{
double leftOffset = 0;
double topOffset = 0;
Pen pen = new Pen(Brushes.Black, 3);
pen.Freeze();
foreach (RowDefinition row in this.RowDefinitions)
{
dc.DrawLine(pen, new Point(0, topOffset), new Point(this.ActualWidth, topOffset));
topOffset += row.ActualHeight;
}
// draw last line at the bottom
dc.DrawLine(pen, new Point(0, topOffset), new Point(this.ActualWidth, topOffset));
//foreach (ColumnDefinition column in this.ColumnDefinitions)
//{
// dc.DrawLine(pen, new Point(leftOffset, 0), new Point(leftOffset, this.ActualHeight));
// leftOffset += column.ActualWidth;
//}
// draw last line on the right
//dc.DrawLine(pen, new Point(leftOffset, 0), new Point(leftOffset, this.ActualHeight));
base.OnRender(dc);
}
}
}
XAML:
<Window x:Class="GridWithBorder.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GridWithBorder"
Title="MainWindow" Height="350" Width="525">
<local:BorderGrid>
<local:BorderGrid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</local:BorderGrid.RowDefinitions>
<local:BorderGrid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</local:BorderGrid.ColumnDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Red" Margin="5" />
<Rectangle Grid.Row="0" Grid.Column="1" Fill="Blue" Margin="5" />
<Rectangle Grid.Row="0" Grid.Column="2" Fill="Orange" Margin="5" />
<Rectangle Grid.Row="0" Grid.Column="3" Fill="Red" Margin="5" />
<Rectangle Grid.Row="1" Grid.Column="0" Fill="Yellow" Margin="5" />
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Green" Margin="5" />
<Rectangle Grid.Row="1" Grid.Column="2" Fill="Purple" Margin="5" />
<Rectangle Grid.Row="1" Grid.Column="3" Fill="Green" Margin="5" />
<Rectangle Grid.Row="2" Grid.Column="0" Fill="Orange" Margin="5" />
<Rectangle Grid.Row="2" Grid.Column="1" Fill="Red" Margin="5" />
<Rectangle Grid.Row="2" Grid.Column="2" Fill="Blue" Margin="5" />
<Rectangle Grid.Row="2" Grid.Column="3" Fill="Red" Margin="5" />
</local:BorderGrid>
</Window>
You could pull that border out into a reusable resource, but I suspect what you're really trying to do is create a GridView.
You can just set the Background property on your Grid. If there is commonality between the border which you are applying to the different rows, you can create a default style (and if desired, limit the scope of this style to the Grid itself):
XAML
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Border}">
<!-- All rows -->
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="CornerRadius" Value="5" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border Grid.Row="0">
<TextBlock Text="This row has a black border (default)." />
</Border>
<Border BorderBrush="Red" Grid.Row="1">
<TextBlock Text="This row has a red border." />
</Border>
<Border BorderBrush="Green" BorderThickness="4" Grid.Row="2">
<TextBlock Text="This has a thick green border." />
</Border>
</Grid>
With a default Style, no additional property needs to be set on your row's Border to achieve a default look (row one above). If a certain row needs to tweak the look and feel, then just provide additional properties on the Border to override the ones set in the default Style (rows two and three above). If this technique is something you are applying across multiple views in your application, then you can extract this style into a separate ResourceDictionary and simply merge it where appropriate.
Hope this helps!