Button within ListBox ItemTemplate not selecting item wpf mvvm - wpf

Hi i have the same problem as here Button within ListBox ItemTemplate not selecting item
And i know the reason why my button doesnt see selected item, it because listbox is called when MouseDown
and button is called on Click. But i dont know how to fix it
XAML
<ListBox Grid.Row="1" x:Name="ListViewProduct" Background="#FFf1f1f1" ItemsSource="{Binding Videos}" >
<ListBox.ItemTemplate>
<!-- FIX Ripple-->
<DataTemplate >
<Border Background="White" Name="border" Margin="50 10 50 10" Width="310" Height="360">
<StackPanel>
<Border Width="300" Height="300" CornerRadius="5" Margin="5">
<Border.Effect>
<DropShadowEffect ShadowDepth="5"/>
</Border.Effect>
<Border.Background>
<ImageBrush ImageSource="{Binding Image}"/>
</Border.Background>
</Border>
<Grid>
<StackPanel>
<TextBlock Margin="5" Text="{Binding Name}" FontSize="14" FontFamily="Franklin Gothic Medium" />
<TextBlock Margin="5 0" Text="{Binding User.Name}" FontSize="13" />
</StackPanel>
<Button HorizontalAlignment="Right" Margin="0 0 10 0"
Command="{Binding ElementName= ListViewProduct,Path=DataContext.DownloadPageCommand}"
CommandParameter="{Binding Path=SelectedItem , ElementName=ListViewProduct}">
<materialDesign:PackIcon Width="25" Height="25" Kind="Download" Foreground="White"/>
</Button>
</Grid>
</StackPanel>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding ElementName= ListViewProduct,Path=DataContext.ViewWatchingPageCommand}"
CommandParameter="{Binding Path=SelectedItem , ElementName=ListViewProduct}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
C#
private RelayCommandParametr _downloadpageCommand;
public RelayCommandParametr DownloadPageCommand
{
get
{
return _downloadpageCommand
?? (_downloadpageCommand = new RelayCommandParametr(
obj =>
{
SelectedVideo = obj as Video;
_navigationService.NavigateTo("Download",obj);
}));
}
}
private RelayCommandParametr _viewWatchingPageCommand;
public RelayCommandParametr ViewWatchingPageCommand
{
get
{
return _viewWatchingPageCommand
?? (_viewWatchingPageCommand = new RelayCommandParametr(
(obj) =>
{
SelectedVideo = obj as Video;
_navigationService.NavigateTo("VideoWatching",SelectedVideo);
}));
}
}

<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
I find answer i just set selected item.

Related

Filter DataGrid columns and pass object to ViewModel

I get this error :
System.Windows.Data Error: 40 : BindingExpression path error: 'OpenPopupCommand' property not found on 'object' ''String' (HashCode=62725275)'. BindingExpression:Path=OpenPopupCommand; DataItem='String'
when I added a parameter to my command:
OpenPopupCommand = new RelayParamCommand((e) => PopupVisibility(FilterButton) );
VM:
private void PopupVisibility(object sender)
{
Console.WriteLine(sender.ToString());
PopupVisible ^= true;
}
Think is that I added Filter Button to Datagrid Headers which are generated automatically. Now I want to open popup when button is clicked. But think is that is not working, because i have to pass button by buttons x:Name to Popup PlacementTarget parameter.
<Page.DataContext>
<PDB:UsersViewModel x:Name="vm"/>
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--Page Header info content-->
<Grid Grid.Row="0">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Text="{Binding ElementName=userPage, Path=Name}"/>
<TextBlock Margin="2" Text="{Binding SelectedUser.Name}"/>
<TextBlock Margin="2" Text="{Binding ElementName=myGrd, Path=CurrentColumn.DisplayIndex}"/>
<Button x:Name="mybtn"
Content="{Binding Filters.Count, Mode=OneWay}"
Visibility="{Binding Filters.Count, Converter={Wpf:VisibilityConverter}}"
/>
</StackPanel>
</Grid>
<!--Datagrid content-->
<DataGrid x:Name="myGrd"
SelectionMode="Single"
SelectionUnit="Cell"
CurrentItem="{Binding SelectedUser, Mode=TwoWay}"
CurrentColumn="{Binding CurrentColumn, Mode=TwoWay}"
IsReadOnly="True"
Grid.Row="1"
ItemsSource="{Binding FilteredUserList}"
AutoGenerateColumns="True"
CanUserAddRows="False"
>
<DataGrid.Resources>
<!--Popup-->
<ContextMenu x:Key="ContextMenu">
<ContextMenu.Items>
<MenuItem Header="Filter by Selection" Command="{Binding IncludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Filter exclude Selection" Command="{Binding ExcludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Remove all Filters" Command="{Binding RemoveAllFiltersCommand, Source={x:Reference vm}}" Visibility="{Binding Filters.Count, Source={x:Reference vm}, Converter={Wpf:VisibilityConverter}}"/>
</ContextMenu.Items>
</ContextMenu>
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader" x:Name="FilterHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" HorizontalAlignment="Center"/>
<Button Name="FilterButton"
Content="[-F-]"
Command="{Binding OpenPopupCommand}"
CommandParameter="{Binding ElementName=FilterButton}"/>
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding PopupVisible}"
PlacementTarget="{Binding FilterButton}">
<Border Background="White" BorderBrush="Black" Padding="5" BorderThickness="2" CornerRadius="5">
<StackPanel Orientation="Vertical">
</StackPanel>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
</Grid>
By this approach i want to pass clicked button as parameter to VM and bind it to Popup PlacementTarget parameter. What i am doing wrong? Can i pass clicked button parameter? I know i break mvvm rules when passing view to vm, but how to achieve what i want, when i don't want to define every column in datagrid. Thank you
Your bindings have the DataContext of the DataGridColumnHeader as source which is the value of DataGridColumn.Header. This value in your case is a string and not your expected view model. This is the reason why your bindings doesn't resolve and you get the error message (which is exactly telling you this).
To fix the binding you have to find the next parent element that has the required DataContext, which is I assume the DataGrid:
<Button Name="FilterButton"
Content="[-F-]"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.OpenPopupCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
When checking your code I can see the command which is bound to the Button is toggling the PopupVisible property. I therefore suggest to remove this view related code from the view model and replace the Button with a ToggleButton which binds directly to the Popup.IsOpen:
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}"
HorizontalAlignment="Center" />
<ToggleButton Name="FilterButton"
Content="[-F-]" />
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
PlacementTarget="{Binding ElementName=FilterButton}">
<Border Background="White"
BorderBrush="Black"
Padding="5"
BorderThickness="2"
CornerRadius="5">
<StackPanel Orientation="Vertical">
</StackPanel>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
To get the cell values of all rows contained in a single column where the column header value is the parameter and maps to a property name requires reflection. You need to bind a ListView to the resulting column value collection which uses an ItemTemplate to add a CheckBox to the item. The final version should be as follows:
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}}"
HorizontalAlignment="Center" />
<ToggleButton Name="FilterButton"
Content="[-F-]"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.GenerateFilterViewItemsCommand)}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridColumnHeader}, Path=Content}" />
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
PlacementTarget="{Binding ElementName=FilterButton}">
<Border Background="White"
BorderBrush="Black"
Padding="5"
BorderThickness="2"
CornerRadius="5">
<ListView
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.FilterViewItems)}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsIncluded, Mode=OneWayToSource}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.IncludeItemCommand)}"
CommandParameter="{Binding}" />
<ContentPresenter Content="{Binding CellValue}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
To map the filter list to the actual items requires an additional type to hold the information:
Predicate.cs
public class FilterPredicate
{
public FilterPredicate(int rowIndex, object cellValue, object columnKey)
{
this.RowIndex = rowIndex;
this.CellValue = cellValue;
this.ColumnKey = columnKey;
}
public int RowIndex { get; set; }
public object CellValue { get; set; }
public bool IsIncluded { get; set; }
public object ColumnKey { get; set; }
}
You also need to add a collection for the filter view and one for the included item indices to the view model:
UsersViewModel.cs
public class UsersViewModel
{
public UsersViewModel()
{
this.FilterViewItems = new ObservableCollection<FilterPredicate>();
this.IncludedItemsIndex = new Dictionary<object, List<int>>();
}
// The binding source for the Popup filter view
public ObservableCollection<FilterPredicate> FilterViewItems { get; set; }
private Dictionary<object, List<int>> IncludedItemsIndex { get; set; }
public ICommand GenerateFilterViewItemsCommand => new RelayParamCommand((param) =>
{
var columnHeader = param as string;
this.IncludedItemsIndex.Remove(columnHeader);
this.FilterViewItems.Clear();
for (var rowIndex = 0; rowIndex < this.FilteredUserList.Count; rowIndex++)
{
var data = this.FilteredUserList.ElementAt(rowIndex);
var columnValue = data.GetType()
.GetProperty(columnHeader, BindingFlags.Public | BindingFlags.Instance)?
.GetValue(data);
if (columnValue != null)
{
this.FilterViewItems.Add(new FilterPredicate(rowIndex, columnValue, columnValue));
}
}
});
public ICommand IncludeItemCommand => new RelayParamCommand((param) =>
{
var predicate = param as FilterPredicate;
if (predicate.IsIncluded)
{
if (!this.IncludedItemsIndex.TryGetValue(predicate.ColumnKey, out List<int> includedIndices))
{
includedIndices = new List<int>();
this.IncludedItemsIndex.Add(predicate.ColumnKey, includedIndices);
}
includedIndices.Add(predicate.RowIndex);
}
else
{
if (this.IncludedItemsIndex.TryGetValue(predicate.ColumnKey, out List<int> includedIndices))
{
includedIndices.Remove(predicate.RowIndex);
}
}
// Apply the filter
CollectionViewSource.GetDefaultView(this.FilteredUserList).Filter =
data => this.IncludedItemsIndex.Values
.SelectMany(indices => indices)
.Contains(this.FilteredUserList.IndexOf(data as FilteredUser));
});
}
Since you are autogenerating columns, I strongly suspect it is trying to find a OpenPopupCommand in one of the string properties on the items for FilteredUserList.
Try The Following :
Give you'r Page or DataGrid a Name, And change your Binding to
<MenuItem Command="{Binding Path=DataContext.IncludeCommand, Source={x:Reference myPage}}"/>

How to change DynamicResource in WPF

I have a UserControl with that XAML:
<StackPanel>
<Label HorizontalContentAlignment="Center">
<Rectangle Name="iconContainer" Height="120" Width="120" Fill="#FF045189">
<Rectangle.OpacityMask>
<VisualBrush Visual="{DynamicResource appbar_disconnect}"/>
</Rectangle.OpacityMask>
</Rectangle>
</Label>
<TextBlock Name="tBlockPortStatus" Foreground="#FF7C7676" FontWeight="Bold" FontSize="15" Margin="3" TextAlignment="Center" HorizontalAlignment="Center" TextWrapping="Wrap">PORT STATUS (Testing wrapping text abilities for this control)</TextBlock>
</StackPanel>
I need to change the icon(named appbar_disconnect) and use another DynamicResource (ex. appbar_connect) using Code Behind or MVVM.
How can I achieve this?
Regards
Your case looks like it would be better handled using a Trigger than a DynamicResource, but if you insisted on using DynamicResource, you basically need to define your states' icons/images as resources in the App.xaml file:
<Application.Resources>
<Image Source="Icons/disconnect.png" x:Key="AppbarDisconnect"/>
<Image Source="Icons/connect.png" x:Key="AppbarConnect"/>
<Image Source="Icons/undefined.png" x:Key="AppbarStatus"/>
</Application.Resources>
Assuming your UserControl looks like that:
<StackPanel>
<Label HorizontalContentAlignment="Center">
<Rectangle Name="IconContainer" Height="120" Width="120" Fill="#FF045189">
<Rectangle.OpacityMask>
<VisualBrush Visual="{DynamicResource AppbarStatus}"/>
</Rectangle.OpacityMask>
</Rectangle>
</Label>
<TextBlock Name="TBlockPortStatus" Foreground="#FF7C7676" FontWeight="Bold" FontSize="15" Margin="3" TextAlignment="Center" HorizontalAlignment="Center" TextWrapping="Wrap">PORT STATUS (Testing wrapping text abilities for this control)</TextBlock>
</StackPanel>
You could update the AppbarStatus resource on a specific event (from the code behind or from the viewmodel) like so:
Application.Current.Resources["AppbarStatus"] = Application.Current.Resources["AppbarDisconnect"];
Update
In case you want to use a DataTrigger, just add a property to hold the connection-status to your user control:
private bool _connetionStatus;
public bool ConnectionStatus
{
get { return _connetionStatus; }
set
{
if (value == _connetionStatus) return;
_connetionStatus = value;
OnPropertyChanged();
}
}
The DataTrigger should be self explanatory:
<StackPanel>
<Label HorizontalContentAlignment="Center">
<Rectangle Name="IconContainer" Height="120" Width="120" Fill="#FF045189">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="OpacityMask">
<Setter.Value>
<VisualBrush Visual="{DynamicResource AppbarDisconnect}"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ConnectionStatus}" Value="true">
<Setter Property="OpacityMask">
<Setter.Value>
<VisualBrush Visual="{DynamicResource AppbarConnect}"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Label>
<TextBlock Name="TBlockPortStatus" Foreground="#FF7C7676" FontWeight="Bold" FontSize="15" Margin="3" TextAlignment="Center" HorizontalAlignment="Center" TextWrapping="Wrap">PORT STATUS (Testing wrapping text abilities for this control)</TextBlock>
</StackPanel>

Modifying TabControl's header

So I'm adding my views directly to the TabControl's Items collection at runtime (instead of creating TabItems around them and addings those TabItems to TabControl). The views expose a property (wrapper around a ViewModel property of the same name) named HasChanges that I want to bind to TabItem's Header to show a Asterisk (*) sign to identify tabs with unsaved changes, just like VS does. I have already tried using DataTemplates but am having trouble accessing the view object in the DataTemplate. What's the correct way of doing this? Here's one of my several attempts:
<TabControl.ItemTemplate>
<DataTemplate DataType="UserControl">
<StackPanel Orientation="Horizontal" Margin="0" Height="22">
<TextBlock VerticalAlignment="Center" Text="{Binding HeaderText, RelativeSource={RelativeSource AncestorType=UserControl}}" />
<TextBlock Text="*" Visibility="{Binding HasChanges, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource B2VConverter}}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
Note that I'm trying two different binding methods for the two TextBlocks, none of which is working. My views inherit from UserControl and expose properties HasChanges and HeaderText.
OK. I solved it myself. For anyone else trying to implement a VS-like Close button and unsaved changes asterisk, here's the template:
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0" Height="22">
<TextBlock VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource AncestorType=TabItem}, Path=Content.HeaderText}" />
<TextBlock Text=" *" ToolTip="Has unsaved changes" Visibility="{Binding Content.DataContext.HasChanges, RelativeSource={RelativeSource AncestorType=TabItem}, Converter={StaticResource B2VConverter}}" />
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Width="18" Height="18"
Margin="6,0,0,0" Padding="0" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center"
Command="{Binding DataContext.TabClosingCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"
VerticalAlignment="Center" Focusable="False">
<Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Line StrokeThickness="3" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="Gray" X1="1" Y1="1" X2="9" Y2="9" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Line StrokeThickness="3" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Stroke="Gray" X1="1" Y1="9" X2="9" Y2="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Button>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
Results in an elegant drawing-based button with a flat-look. Your View must implement Boolean HasChanges and HeaderText properties, plus you need to define a BooleanToVisibilityConverter in your resources section, named B2VConverter.

Textbox inside listbox selecteditem.. how modify textbox content?

I have a listbox for inventory. When I select an item it shows a couple of controls to edit the volume or amount of items. like this:
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" >
<Label Content="Edit Volume:"/>
<Button Click="bPlus_Click2" Content="+" Height="29" Margin="10,0,0,0" Name="bPlus" Width="29" />
<TextBox FontSize="16" Height="29" HorizontalContentAlignment="Center" IsReadOnly="True" Name="tNum2" Text="0" VerticalContentAlignment="Center" Width="44" />
<Button Click="bMinus_Click2" Content="-" Height="29" Name="bMinus" Width="29" />
<Button Content="OK!"/>
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}}}" Value="False">
<Setter Property="StackPanel.Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
Now I need the PLUS and MINUS buttons to edit the content of the textbox.. how can i do it??
I FOUND THE WAY!!
THIS IS WHAT I HAVE:
<Window.Resources>
<DataTemplate x:Key="dtInventory">
<Border Name="itemBorder" BorderBrush="#FFEBE476" BorderThickness="2" Padding="10" Margin="2" Background="#FF5AB11D">
<StackPanel x:Name="sp1">
<StackPanel Orientation="Horizontal" x:Name="spsp1">
<StackPanel Width="60">
<TextBlock Text="DME Item: "/>
</StackPanel>
<StackPanel Width="205" x:Name="spsp2">
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
<StackPanel Width="60" x:Name="spsp3">
<TextBlock Text="Volume: "/>
</StackPanel>
<StackPanel Width="30" x:Name="spsp4">
<TextBlock Text="{Binding Path=Volume}"/>
</StackPanel>
</StackPanel>
<StackPanel x:Name="sp2" HorizontalAlignment="Right" Orientation="Horizontal" >
<Label Content="Edit Volume:" x:Name="l1"/>
<myext:IntegerUpDown x:Name="udVolume" Minimum="0" DefaultValue="0"/>
<Button Content="OK!" x:Name="bOk" Click="bOk_Click" />
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}}}" Value="False">
<Setter Property="StackPanel.Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
Then the Listbox:
<ListBox Height="399" HorizontalAlignment="Left" Margin="462,61,0,0" Name="lInventory" VerticalAlignment="Top" Width="390" ItemsSource="{Binding}" ItemTemplate="{StaticResource dtInventory}">
And the .cs:
ListBoxItem selecteditem = lInventory.ItemContainerGenerator.ContainerFromIndex(int.Parse(lInventory.SelectedIndex.ToString())) as ListBoxItem;
if (selecteditem != null)
{
try
{
DataTemplate dt = selecteditem.ContentTemplate;
Border border = VisualTreeHelper.GetChild(selecteditem, 0) as Border;
ContentPresenter cp = border.Child as ContentPresenter;
StackPanel sp = dt.FindName("sp1", cp) as StackPanel;
IntegerUpDown updown = sp.FindName("udVolume") as IntegerUpDown;
if (updown.Value != 0)
{
Inventory.DMEItems dme = new Inventory.DMEItems();
dme.Volume = int.Parse(updown.Value.ToString());
dme.DMEInventoryItemID = int.Parse(lInventory.SelectedValue.ToString());
dme.UpdateItem();
UpdateInventory();
}
}
catch (Exception ex)
{ System.Windows.MessageBox.Show("ERROR: " + ex.Message, "Edit Volume" ,MessageBoxButton.OK, MessageBoxImage.Error); }
}
I hope it works for other people!
Unfortunately you can't do Mathematic oeprations in xaml out of the box.
But you could use a Numeric UpDown control (e.g. from the Extented WPF Toolkit)
Or you make your own UserControl where you have your two buttons and your textbox along with the functionality to count up/down.
Then use that control in your markup e.g.
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" >
<Label Content="Edit Volume:"/>
<local:MyNumericUpDownControl/>
EDIT:
Here's a link on how to create your own numericupdown control

Can we have buttons in a validation template, and how can we bind it to the ViewModel method?

Requirement:
Need to display an error message when the user types a forlder name that doesn't exist as shown below:
Problem: I am able to display the UI but not able to call a method in the view model when the user clicks on the button "CreateNew"
View Model Code:
public string this[string columnName]
{
get { return "The entered folder name doesn't exist."; }
}
RelayCommand createNewFolder;
public RelayCommand CreateNewFolder
{
get
{
if (createNewFolder == null)
createNewFolder = new RelayCommand(param => this.OnCreateNewFolder());
return createNewFolder;
}
}
public void OnCreateNewFolder()
{
MessageBox.Show("CreateNewFolder");
}
RelayCommand.cs can be downloaded at: http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=mag200902MVVM&DownloadId=4357
Xaml Code:
<Window.Resources>
<ControlTemplate x:Key="validationTemplate">
<DockPanel LastChildFill="True">
<Border Margin="5,5,0,0" DockPanel.Dock="Bottom" Background="Red">
<StackPanel>
<TextBlock Name="ErrorText" Foreground="White" Background="Red"
FontSize="12" Padding="2" FontFamily="Trebuchet MS"
TextWrapping="Wrap"
Text="{Binding [0].ErrorContent}" ></TextBlock>
<StackPanel Margin="0" Orientation="Horizontal">
<Button Content="Create New" Command="{Binding Path=CreateNewFolder}" Margin="10" Padding="5"></Button>
<Button Content="Cancel" Margin="10" Padding="5" ></Button>
</StackPanel>
</StackPanel>
</Border>
<AdornedElementPlaceholder Name="ErrorTextBox" />
</DockPanel>
</ControlTemplate>
<Style x:Key="ValidationStyle" TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BitmapEffect">
<Setter.Value>
<BitmapEffectGroup>
<OuterGlowBitmapEffect GlowColor="Red" GlowSize="3" Noise="0.6"></OuterGlowBitmapEffect>
</BitmapEffectGroup>
</Setter.Value>
</Setter>
<Setter Property="DataContext"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},Path=DataContext}">
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<AdornerDecorator >
<ComboBox IsEditable="True" FontSize="11" Margin="10" Width="250"
VerticalAlignment="Center"
Text="{Binding Path=StrText, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource ValidationStyle}"></ComboBox>
</AdornerDecorator>
</Grid>
Please note that i set the DataContext property in the style:
<Setter Property="DataContext" Value="{Binding RelativeSource={x:Static
RelativeSource.Self},Path=DataContext}">
</Setter>
Please let me know how to bind the method to a button in the validation template.
You could reference the DataContext for the AdornerDecorators Child in the Binding. I think something like this will work
<Button Content="Create New"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type AdornerDecorator}},
Path=Child.DataContext.CreateNewFolder}"
Margin="10" Padding="5"></Button>
This line looks suspicious:
createNewFolder = new RelayCommand(param => this.OnCreateNewFolder());
Maybe you should replace it by:
createNewFolder = new RelayCommand(OnCreateNewFolder);

Resources