I've setup a ListView with grouping and I would like to retrieve the GroupName when I right click on the group in MVVM. I've placed a ContextMenu on my group style, and I was trying to use the EventToCommand from System.Windows.Interactivity to get the underlying item.
Here the relevant part:
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False" Template="{StaticResource CustomizedExpander}" Background="#FFEBEBEB" BorderThickness="0" ContextMenu="{StaticResource GroupContextMenu}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseRightButtonDown">
<i:InvokeCommandAction Command="{Binding Path=OnCategorySelected}" CommandParameter="{Binding Name}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<!--N.B. The "Name" property is part of the CollectionViewSource-->
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF767676" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" Foreground="#FF454545" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" Foreground="#FF767676" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
I don't know if it's the right way to do it but it seems the command is not triggered at all.
Any suggestion?
UPDATE:
The whole thing was much simpler than I thought. The interaction part was not required at all. Fixing the binding is enough to get the underlying category when the context menu is shown:
<ListView ItemsSource="{Binding Modifications}" SelectedItem="{Binding SelectedItem}">
<ListView.Resources>
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Execute" Command="{Binding Path=DataContext.OnExecuteScript, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" Visibility="{Binding CanExecute, Converter={StaticResource BooleanToVisibilityConverter}}" />
<MenuItem Header="Execute up to this" Command="{Binding Path=DataContext.OnExecuteScriptUpToThis, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" Visibility="{Binding CanOnlyBeExecutedSequentially, Converter={StaticResource BooleanToVisibilityConverter}}" />
<MenuItem Header="Drop" Command="{Binding Path=DataContext.OnExecuteDrop, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Visibility="{Binding Drop, Converter={StaticResource BooleanToVisibilityConverter}}" Background="WhiteSmoke" />
<MenuItem Header="Dump" Command="{Binding Path=DataContext.OnExecuteDump, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Visibility="{Binding CanDump, Converter={StaticResource BooleanToVisibilityConverter}}" Background="WhiteSmoke" />
</ContextMenu>
<ContextMenu x:Key="GroupContextMenu">
<MenuItem Header="Dump all" Command="{Binding Path=DataContext.OnExecuteDumpAll, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" CommandParameter="{Binding Name}" Background="WhiteSmoke" />
</ContextMenu>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Type" Width="120" DisplayMemberBinding="{Binding Type}" />
<GridViewColumn Header="Object Name" Width="Auto" DisplayMemberBinding="{Binding DisplayName}" />
<GridViewColumn Header="" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Deploy}" IsHitTestVisible="False" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Object Name" Width="300" DisplayMemberBinding="{Binding ObjectName}" />
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}" >
<Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Drop}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="ContextMenu" Value="{StaticResource GroupContextMenu}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False" Template="{StaticResource CustomizedExpander}" Background="#FFEBEBEB" BorderThickness="0">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<!--N.B. The "Name" property is part of the CollectionViewSource-->
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF767676" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" Foreground="#FF454545" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" Foreground="#FF767676" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
First of all, i think i've figured out why your Command isnt firing.
Since you are in an Template, the DataContext has Changed. Therefore your CommandBinding should look like this:
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.OnCategorySelected}" CommandParameter="{Binding}"/>
Also, your CommandParameter should not be Name because you'll only get a string in the end instead the whole Object.
If you use my above code you will get the whole CollectionViewGroup instead. In said CollectionViewGroup you'll find all the Items in the group. If you are fine with getting just the Groupname you can proceed with you implementation of course.
I dont really understand why you are using a ContextMenu and what it does (Since you dont posted that code), but if you are interested in how you can display the grouped Items in such a ContextMenu, you can do it like this:
<Expander IsExpanded="False" Background="#FFEBEBEB" BorderThickness="0" >
<Expander.ContextMenu>
<ContextMenu ItemsSource="{Binding Items}"/>
</Expander.ContextMenu>
</Expander>
Since we now know, what we have to deal with (It's still an CollectionViewGroup) we can set the Items of it as ItemsSource of the ContextMenu.
Hope this helps!
Related
I am binding my listview to a viewmodel using Caliburn. My view's code is the following:
<ListView x:Name="ListView" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding ProjectsPMod}" Margin="110,0,110,131" HorizontalContentAlignment="Stretch" BorderThickness="0" Height="111" VerticalAlignment="Bottom">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="SelectionChanged">
<cal:ActionMessage MethodName="OpenProjectShell">
<cal:Parameter Value="{Binding ElementName=ListView, Path=SelectedItem}" />
</cal:ActionMessage>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{StaticResource Appbar_Suitcase}" />
<Label Content="{Binding Name}"/>
</StackPanel>
<Separator HorizontalAlignment="Stretch" Margin="0, 10, 0, 0"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Right now each item in every row just displays its name and a briefcase icon. If I wanted to add an "Edit"further down that row, where if you clicked on it something happened (perhaps a popup appears), how would I do it?
You can make an ordinary Button look like a link using a template. Just add a Button to your StackPanel and bind it to a command:
<ListView x:Name="ListView" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding ProjectsPMod}" Margin="110,0,110,131" HorizontalContentAlignment="Stretch" BorderThickness="0" Height="111" VerticalAlignment="Bottom">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="SelectionChanged">
<cal:ActionMessage MethodName="OpenProjectShell">
<cal:Parameter Value="{Binding ElementName=ListView, Path=SelectedItem}" />
</cal:ActionMessage>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{StaticResource Appbar_Suitcase}" />
<Label Content="{Binding Name}"/>
</StackPanel>
<Separator HorizontalAlignment="Stretch" Margin="0, 10, 0, 0"/>
<Button Margin="0 10 0 0" Content="Link" Cursor="Hand" Command="{Binding YourCommand}">
<Button.Template>
<ControlTemplate TargetType="Button">
<TextBlock TextDecorations="Underline">
<ContentPresenter />
</TextBlock>
</ControlTemplate>
</Button.Template>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Foreground" Value="Blue" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You can control the position of the Button within the StackPanel using its Margin property.
If you mean "How to add an edit button for each row?" then you simply need to add a button in the ItemTemplate:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{StaticResource Appbar_Suitcase}" />
<Label Content="{Binding Name}"/>
<!-- Edit button -->
<Button Command="{Binding EditCommandOnViewModel}" Content="Edit" />
</StackPanel>
<Separator HorizontalAlignment="Stretch" Margin="0, 10, 0, 0"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
I am working with a Wpf/XAML based application which has DataGrid with one of the DataGridColumn containing the TreeView control to let user select the item required.
<DataGrid.Columns>
<DataGridTextColumn Header="SerialNumber" Width="*" Binding="{Binding SerialNumber}" />
<DataGridTemplateColumn Header="Field" Width="*">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<uControls:FieldTreeViewControl />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding CurrentField.FieldName,Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Field column in above code is the one which displays treeviewcontrol in the DataGrid cell by referencing the FieldTreeViewControl and it works perfectly. The xaml code of FieldTreeViewControl is :
<UserControl>
.......
<Grid>
<TreeView x:Name="MyUITreeView" ItemsSource="{Binding Fields}">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsNodeSelected, Mode=TwoWay}" />
</Style>
<DataTemplate x:Key="NormalTemplate">
<StackPanel Orientation="Horizontal">
<!--<TextBlock Text="-" Margin="3"/>-->
<TextBlock Text="{Binding FieldName}" Margin="3"/>
<StackPanel.ContextMenu>
<ContextMenu Name="myChildMenu" DataContext="{Binding PlacementTarget,RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add Field" Command="{Binding DataContext.AddFieldCommand}" CommandParameter="{Binding}">
</MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
<!--<DataTemplate x:Key="EditTemplate">
<TextBox Text="{Binding FieldName}"/>
</DataTemplate>-->
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<ContentPresenter Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="ContentTemplate" Value="{StaticResource NormalTemplate}"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding IsNodeSelected,
RelativeSource={RelativeSource
FindAncestor,
AncestorType={x:Type TreeViewItem}}}"
Value="True">
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
Now when user completes selecting an item from treeview I want to collapse treeview to show only that selected item. Later when user wants to change his selection , then the treeview should be available again by clicking on that column/cell. Is that possible to do this in wpf/XAML ? I tried to follow the link but not able to implement it for my scenario.
I'm trying to compose a data grid (in an MVVM 4.5 project) that will show my data grouped by a specific property (by use of expanders). This all works but I'd like to add a context menu with the options "Expand All" and "Collapse All" which will collapse/expand all the groups. The event handlers for this menu item click events are being handled in the window's code-behind.
The problem is my context menu is applied to the expander and thus inherited by all its children which includes the <ItemsPresenter/> and this all rows/cells.
I want to only apply the context menu to the grouped header itself. This is achievable if it's applied to the innards (like the StackPanel in the example below) but in that case, the context menu isn't accessible for the entire Header line, only on the StackPanel contents/text.
I'm planning on using a different context menu for the items themselves (add/edit etc) and have a Collapse/Expand context menu only apply to the group header. Is this achievable?
<DataGrid Name="dgData" ItemsSource="{Binding MyItems}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.Resources>
<!--GroupHeader Text-->
<Style x:Key="gridGroupTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
<!--GroupHeader ContextMenu-->
<ContextMenu x:Key="cm_columnHeaderMenu">
<MenuItem Name="mi_ExpandAll" Header="Expand groups"/>
<MenuItem Name="mi_CollapseAll" Header="Collapse groups"/>
</ContextMenu>
<!--Grouping style-->
<Style x:Key="filesGroupHeaderStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp" IsExpanded="true">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="ContextMenu" Value="{StaticResource cm_columnHeaderMenu}"/>
</Style>
</Expander.Style>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Style="{StaticResource gridGroupTextStyle}"/>
<TextBlock Text=" (" Style="{StaticResource gridGroupTextStyle}"/>
<TextBlock Text="{Binding ItemCount}" Style="{StaticResource gridGroupTextStyle}"/>
<TextBlock Text=")" Style="{StaticResource gridGroupTextStyle}"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource filesGroupHeaderStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Column 1" Binding="{Binding Prop1}" IsReadOnly="True"/>
<DataGridTextColumn Header="Column 2" Binding="{Binding Prop2}" IsReadOnly="True"/>
<DataGridTextColumn Header="Column 3" Binding="{Binding Prop3}" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
If i understand you right, you want to click anywhere in your StackPanel and get the ContextMenu to work like this:
If yes, here we go in code:
<Expander Grid.Row="1">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="ContextMenu" Value="{x:Null}"/>
</Style>
</Expander.Style>
<Expander.Header>
<StackPanel Orientation="Horizontal" Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}}" Background="Pink" HorizontalAlignment="Stretch">
<StackPanel.Style>
<Style TargetType="StackPanel" >
<Setter Property="ContextMenu" Value="{StaticResource cm_columnHeaderMenu}"></Setter>
</Style>
</StackPanel.Style>
<TextBlock Text="Hello" />
<TextBlock Text=" (" />
<TextBlock Text="world" />
<TextBlock Text=")" />
<TextBlock Text="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}}" />
</StackPanel>
</Expander.Header>
<TextBlock Text="Dummy"></TextBlock>
</Expander>
What did i do?
First, bind your Stackpanel-Width to actual width of Expander
Second, make the Stackpanel stretch.
Hope this helps
In my xaml form i have a dropdown button the dropdown values will bind from database and my requirement is i want to add one image for each dropdown value in the dropdown box, how can get this.....
<ctrl:DropDownButton Grid.Column="1"
Height="25"
Text="Add Question"
Width="125"
Margin="5,0,10,0">
<ctrl:DropDownButton.DropDownContextMenu>
<ContextMenu ItemsSource="{Binding Source={StaticResource QuestionTypes}}">
<ContextMenu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="WhiteSmoke"/>
</ItemsPanelTemplate>
</ContextMenu.ItemsPanel>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.AddQuestionCommand,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
<ContextMenu.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,-50,0">
<TextBlock Grid.Column="1" Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
</Grid>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ctrl:DropDownButton.DropDownContextMenu>
</ctrl:DropDownButton>
*I wrote this outside of VisualStudio so please check the syntax
you should add an Image element to your DataTempalte and using a converter for the enum value resolve the path to the image you would like to present.
<ContextMenu.ItemTemplate>
<DataTemplate>
<Grid Margin="-20,0,-50,0">
<Grid>
<Grid.ColumnDefenitions>
<ColumnDefenition />
<ColumnDefenition />
</Grid.ColumnDefenitions>
<Image Source="{Binding Path=., Converter={StaticResource EnumImageConverter}}" />
<TextBlock Grid.Column="1" Text="{Binding Path=., Converter={StaticResource EnumDescriptionConverter}}"/>
</Grid>
</DataTemplate>
</ContextMenu.ItemTemplate>
I am working on a C# WPF project and I have a list view which uses data binding and a collection view.
Below is how my ListView is currently populated.
<ListView HorizontalAlignment="Left" Margin="547,12,0,41" Name="lstCallLogInformation" Width="320">
<ListView.Style>
<Style TargetType="ListView">
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListView">
<TextBlock Text="No items in your devices call log" FontWeight="Bold" HorizontalAlignment="Center" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Foreground="Gray" Text="{Binding Name}" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Control.HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Style="{StaticResource onmouseover}">
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal" Tag="{Binding logID}">
<Image Height="30" Source="{Binding base64ImageString, Converter={StaticResource ImageConverter}}" Width="30" />
<StackPanel Margin="10" Orientation="Vertical">
<TextBlock FontWeight="Bold" Text="{Binding contactNameOrPhoneNumber}" />
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" Text="{Binding dateString}" />
<TextBlock Margin="15, 0, 0, 0" Text="Duration: " />
<TextBlock HorizontalAlignment="Right" Text="{Binding humanReadableCallDuration}" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Vertical">
<Button Background="Transparent" BorderThickness="0" BorderBrush="Transparent" Tag="{Binding logID}" Name="btnAddAsContact" Click="btnAddAsContact_Click">
<Image Width="15" Height="15" Source="/BoardiesSMSServer;component/images/add.png" Tag="{Binding logID}" />
</Button>
<Button Background="Transparent" BorderThickness="0" BorderBrush="Transparent" Tag="{Binding logID}" Name="btnDeleteContactFromLog" Click="btnDeleteContactFromLog_Click">
<Image Width="15" Height="15" Source="/BoardiesSMSServer;component/images/delete.png" Margin="0,0,0,5" Tag="{Binding logID}" />
</Button>
</StackPanel>
</StackPanel>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Within my DockPanel within the DataTemplate I have a StackPanel which contains various TextBlocks from the array that is passed into the binding.
In one of the StackPanels you will see you will see a TextBlock that binds to the element of contactNameOrPhoneNumber.
What I want to be able to do is if another value from the binding is null then I add a TextBlock for the contactNameOrPhoneNumber` element and alongside it have another TextBlock which contains another element of number``, if the other binding value is not null then this extra textblock of number is not added.
So in simple terms, what I want to be able to do is within the DataTemplate have a conditional if statement of if binding value == null add textblock to stackpanel else don't add textblock.
You can do that with Style + Triggers something like this:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding PropertyName}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Also you can create IValueConverter say ObjectToVisibilityConverter which will take input your property and if property is null return Visibility.Collapsed else return Visibility.Visible.
You can then bind property with Visibility value of TextBlock via Converter.
<TextBlock Visibility="{Binding PropertyName,
Converter={StaticResource ObjectToVisibilityConverter}}"/>