WPF Styling ContentControl in Listview - c#

I have this style for ContentControl:
<UserControl.Resources>
<DataTemplate x:Key="textbox">
<TextBox Text="edit me"/>
</DataTemplate>
<DataTemplate x:Key="textblock">
<TextBlock Text="can't edit"/>
</DataTemplate>
<Style x:Key="ContentControlStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Content" Value="{Binding}"/>
<Setter Property="ContentTemplate" Value="{StaticResource textblock}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem},AncestorLevel=1}}"
Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource textbox}" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
And that codes:
<ListView.View>
<GridView x:Name="UGridview1">
<GridViewColumn Width=" 90">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl >
</ContentControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
But I want to create the columns dynamically, so I wrote the following codes:
for (x = 0; x <= Lvobj.obj.Length - 1; x++) // ClmnCount - 1
{
GridViewColumn_ = new GridViewColumn();
GridViewColumn_.SetValue(NameProperty, "Column" + x);
GridViewColumn_.Header = Lvobj.obj(x)(clmntxt);
GridViewColumn_.Width = 99;
/// This part doesnt work
ContentControl cntr = new ContentControl();
cntr.Style = this.Resources("ContentControlStyle");
///
GridViewColumn_.CellTemplate = cntr.ContentTemplate;
UGridview1.Columns.Add(GridViewColumn_);
}
It never works. What must i do for i can create columns with ContentControl Style?

Either use XamlReader.Parse API with a DynamicResource:
const string Xaml = #"<DataTemplate " +
#"xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""> " +
#"<ContentControl Style=""{DynamicResource ContentControlStyle}"" />" +
"</DataTemplate>";
DataTemplate DataTemplate_ = System.Windows.Markup.XamlReader.Parse(Xaml) as DataTemplate;
GridViewColumn_.CellTemplate = DataTemplate_;
Or create a FrameworkElementFactory:
FrameworkElementFactory cc = new FrameworkElementFactory(typeof(ContentControl));
cc.SetValue(ContentControl.StyleProperty, this.Resources["ContentControlStyle"]);
GridViewColumn_.CellTemplate = new DataTemplate() { VisualTree = cc };

Access the ResourceDictionary with square brackets (it's a Dictionary):
this.Resources["ContentControlStyle"]
Make sure to not lookup the style before UserControl.OnInitialized is called. You can override OnInitialized in your UserControl and then initialize the ListView. Alternatively handle the Loaded event.
Alternatively (recommended), consider to define the style implicit (without a x:key). Then the style will be applied automatically, once the target is loaded. This way you don't have to deal with the resources.
You can limit the scope by defining it inside the ResourceDictionary of the ListView:
<ListView>
<ListView.Resources>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Content"
Value="{Binding}" />
<Setter Property="ContentTemplate"
Value="{StaticResource textblock}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem},AncestorLevel=1}}"
Value="True">
<Setter Property="ContentTemplate"
Value="{StaticResource textbox}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.View>
<GridView x:Name="UGridview1">
<GridViewColumn Width=" 90">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>

Related

DataAnnotations OrderBy Length Errors

This method returns property errors (dataannotations). What I want to do is to return those errors sorted by length (ValidationMessage.Length). The list is sorted, but the problem is in the view, it shows them disordered. Can you please help me? Thank you
public void Validate(object currentInstance, string propertyName)
{
if (_validationErrors.ContainsKey(propertyName))
{
_validationErrors.Remove(propertyName);
}
var propertyInfo = currentInstance.GetType().GetProperty(propertyName);
var propertyValue = propertyInfo.GetValue(currentInstance, null);
var validationAttributes = propertyInfo.GetCustomAttributes(true).OfType<ValidationAttribute>();
var validationErrors =
validationAttributes
.Select(
x => new CustomErrorType
{
ValidationMessage = x.FormatErrorMessage(string.Empty),
Severity = x.IsValid(propertyValue) ? Severity.SUCCESS : Severity.ERROR
}
).ToList().OrderBy(x => x.ValidationMessage.Length);;
if (validationErrors.Any(x => x.Severity == Severity.ERROR))
{
_validationErrors.Add(propertyName, validationErrors);
}
}
.xaml
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent.ValidationMessage}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground"
Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorContent.Severity}"
Value="{x:Static customEnums:Severity.WARNING}">
<Setter Property="Foreground"
Value="Orange" />
</DataTrigger>
<DataTrigger Binding="{Binding ErrorContent.Severity}"
Value="{x:Static customEnums:Severity.SUCCESS}">
<Setter Property="Foreground"
Value="DarkGreen" />
<Setter Property="FontWeight"
Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
You could bind to a CollectionViewSource that sorts the validation messages:
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" Source="{Binding}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="ErrorContent" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<AdornedElementPlaceholder x:Name="textBox" />
<ItemsControl ItemsSource="{Binding Source={StaticResource cvs}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorContent.Severity}"
Value="{x:Static customEnums:Severity.WARNING}">
<Setter Property="Foreground"
Value="Orange" />
</DataTrigger>
<DataTrigger Binding="{Binding ErrorContent.Severity}"
Value="{x:Static customEnums:Severity.SUCCESS}">
<Setter Property="Foreground"
Value="DarkGreen" />
<Setter Property="FontWeight"
Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>

XAML GridView Datatrigger won't update

I have this XAML in a WPF project.
<ListView Grid.Column="0" Grid.Row="3" Grid.RowSpan="4" Grid.ColumnSpan="2" ItemsSource="{Binding FilteredApps}" SelectedItem="{Binding SelectedApp, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Status">
<GridViewColumn.CellTemplate >
<DataTemplate DataType="{x:Type Image}">
<Image>
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding State, Mode=TwoWay}" Value="{x:Static common:Globals+ModelState.Unedited}">
<Setter Property="Source" Value="../Resources/IndraDoc/AppUnedited.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding State, Mode=TwoWay}" Value="{x:Static common:Globals+ModelState.Edited}">
<Setter Property="Source" Value="../Resources/IndraDoc/AppEdited.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="{x:Static common:Globals+ModelState.New}">
<Setter Property="Source" Value="../Resources/IndraDoc/AppNew.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="{x:Static common:Globals+ModelState.Deleted}">
<Setter Property="Source" Value="../Resources/IndraDoc/AppDeleted.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Namn" Width="Auto"/>
</GridView>
</ListView.View>
</ListView>
When property "Name" changes the appropriate change is displayed in the Grid, but when the property "State" changes the Image is not switched. Any takes on this?
EDIT
Strange thing is this, I edited the XAML and added the image outside of the ListView bound it to the SelectedApp instead, this works fine, however that was not the intended functionality. It will work for now, but I am still curious as to why my original code won't trigger.
Adding the code where I do the statechange:
private void ChangeStateOnSelected(Globals.ModelState newState)
{
if (SelectedApp.State != newState) //dont do any changework if new state is same as current sate
{
if (SelectedApp.State == Globals.ModelState.New && newState == Globals.ModelState.Edited) //this is not allowed
return;
SelectedApp.State = newState;
if (newState == Globals.ModelState.Unedited)
{
var selectedId = SelectedApp.Id;
_allApps.Replace(_allApps.FirstOrDefault(x => x.Id == SelectedApp.Id), SelectedApp);
_selectedApp = (Data.DataModel.App)_locationService.GetApp(selectedId);
_filteredApps.Replace(_filteredApps.FirstOrDefault(x => x.Id == SelectedApp.Id), SelectedApp);
}
NotifyOfPropertyChange(() => SelectedApp);
NotifyOfPropertyChange(() => AllApps);
NotifyOfPropertyChange(() => FilteredApps);
}
}

wpf Listview with textbox error

I got a listview with a gridview cell template, as a textbox.
I can change the itemssource in the ui at runtime, but I can't do it, in code behind..
Please any ideas why would be a appreciated.
it's bound to a custom list.
this is my code so far:
Xaml template.
<Style TargetType="{x:Type TextBlock}" x:Key="GridBlockStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}, Converter={StaticResource boolToVis}, ConverterParameter=False}" />
</Style>
<Style TargetType="{x:Type FrameworkElement}" x:Key="GridEditStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}, Converter={StaticResource boolToVis}, ConverterParameter=True}" />
</Style>
<DataTemplate x:Key="txt_Field" DataType="{x:Type GridViewColumn}">
<Grid>
<TextBlock Text="{Binding Path=txt}" Style="{StaticResource GridBlockStyle}"/>
<TextBox Style="{StaticResource GridEditStyle}" Text="{Binding Path=txt}"/>
</Grid>
</DataTemplate>
Code behind:
DataTemplate t_txt = (DataTemplate)window.FindResource("txt_Field");
gridView.Columns.Add(new GridViewColumn { Header = "txt", CellTemplate = t_txt });
it updates the itemssource and as soon as the foreach loops are done the Entries are being overriden to null.
try
{
foreach (Items1 item in TempList)
{
foreach (Items1 item2 in list)
{
if (item.num == item2.num)
{
item2.txt = item.txt;
}
}
}
listview.Items.Refresh();
}
catch { }
I found a solution, but I still want to know why it happent..
this is the solution:
for (int i = 0; i < TempList.Count; i++)
{
if (Order_TempList[i].txt == ((Items1)listview.SelectedItem).txt)
{
((Items1)listview.SelectedItem).txt = Order_TempList[i].txt;
i = TempList.Count;
}
}
I can see the error very clearly now.
It's because there is two lists and the visual list (listview.Items) is being updated and not the physical list (TempList).

Cannot change the style of the selected item in a ListBox

I´m trying to change the focused/selected item of a ListBox. My application is based on this article.
At the moment I´m trying to set the ListBoxItem style via data templates:
<DataTemplate x:Key="ItemTemplate">
<TextBlock Text="{Binding}"
Foreground="Black"
FontFamily="Segoe UI"
FontSize="22"
HorizontalAlignment="Left"
Padding="15,10,0,0"
/>
</DataTemplate>
<DataTemplate x:Key="SelectedTemplate">
<TextBlock Text="{Binding}"
Foreground="Red"
FontFamily="Segoe UI"
FontSize="30"
HorizontalAlignment="Left"
Padding="15,10,0,0"
/>
</DataTemplate>
My idea was to switch between those templates using a trigger:
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource SelectedTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
The ListBox looks like this:
<ListBox x:Name="valuesItemsCtrl"
BorderThickness="0"
ItemContainerStyle="{StaticResource ContainerStyle}"
Background="Transparent"
Tag="{Binding }">
<ListBox.AlternationCount>
<Binding>
<Binding.Path>Values.Count</Binding.Path>
</Binding>
</ListBox.AlternationCount>
<ListBox.ItemsSource>
<Binding>
<Binding.Path>Values</Binding.Path>
</Binding>
</ListBox.ItemsSource>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
At the end I add the template to another ListBox:
<ListBox x:Name="tumblersCtrl"
BorderThickness="0"
Background="Transparent"
ItemsSource="{Binding Tumblers, ElementName=thisCtrl}"
ItemTemplate="{StaticResource TumblerTemplate}">
</ListBox>
Thanks for any help or hint!
Use ItemTemplate and data triggers:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
Foreground="Black"
FontFamily="Segoe UI"
FontSize="22"
HorizontalAlignment="Left"
Padding="15,10,0,0"
x:Name="tbName"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="tbName" Property="Foreground" Value="Red"/>
<Setter TargetName="tbName" Property="FontSize" Value="30"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where bound data is a collection of:
public class ViewModel : ViewModelBase
{
public String Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
OnPropertyChanged("Name");
}
}
}
private String name;
public Boolean IsSelected
{
get { return isSelected; }
set
{
if (isSelected != value)
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
private Boolean isSelected;
}
Window code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new[]
{
new ViewModel { Name = "John", IsSelected = true },
new ViewModel { Name = "Mary" },
new ViewModel { Name = "Pater" },
new ViewModel { Name = "Jane" },
new ViewModel { Name = "James" },
};
}
}
If you want to change the templates use DataTemplateSelector.
Dismiss your ContainerStyle and instead set the ListBox.ItemsTemplateSelector to your custom datatemplateselector as static resource.
You can find a detailed example in this link.
EDIT :
According to your comment you don't need DataTemplate just set these two properties in the Trigger:
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="22" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Padding" Value="15,10,0,0" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontSize" Value="30" />
</Trigger>
</Style.Triggers>
</Style>

How to add a tooltip for a datagrid header, where the header text is generated dynamically?

I need to add a tooltip for a column header of a DataGrid (Silverlight 4). I will generate the number of columns and column header text dynamically.
GridColumnCreation(....)
{
IEnumerable allHeaderText = /* Linq query */;
}
How to use this collection to set a tooltip?
This can be done even more simply than in #Farukh's answer:
<data:DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ToolTipService.ToolTipProperty"
Value="Your tool tip here" />
</Style>
</data:DataGridTextColumn.HeaderStyle>
Or, if you need to do it in code:
var style = new Style(typeof(DataGridColumnHeader));
style.Setters.Add(new Setter(ToolTipService.ToolTipProperty,
"Your tool tip here"));
column.HeaderStyle = style;
In case it might help anyone. It works when using TooTip property.
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ToolTip" Value="{Binding}"/>
</Style>
</DataGridTextColumn.HeaderStyle>
If you do not want to create a new style for the Header, simply add a TextBlock for your column header and set the tooltip on it.
<DataGridTextColumn>
<DataGridTextColumn.Header>
<TextBlock Text="ColumnA" ToolTip="ColumnA Tooltip"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>
This can be done by using DataGridTextColumn & DataGridTextColumn.HeaderStyle. In the headerstyle tag, use the ToolTipService and bind the content to the dynamic values generated. Here's a sample code for this...
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="First Name" Binding="{Binding FName}" >
<data:DataGridTextColumn.HeaderStyle>
<Style TargetType="dataprimitives:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ContentControl Content="{Binding}">
<ToolTipService.ToolTip>
<ToolTip Content="Tooltip First" />
</ToolTipService.ToolTip>
</ContentControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGridTextColumn.HeaderStyle>
</data:DataGridTextColumn>
<data:DataGridTextColumn Header="Last Name" Binding="{Binding LName}">
<data:DataGridTextColumn.HeaderStyle>
<Style TargetType="dataprimitives:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ContentControl Content="{Binding}">
<ToolTipService.ToolTip>
<ToolTip Content="Tooltip Second"></ToolTip>
</ToolTipService.ToolTip>
</ContentControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGridTextColumn.HeaderStyle>
</data:DataGridTextColumn>
<data:DataGridTextColumn Header="City" Binding="{Binding City}">
<data:DataGridTextColumn.HeaderStyle>
<Style TargetType="dataprimitives:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ContentControl Content="{Binding}">
<ToolTipService.ToolTip>
<ToolTip Content="Tooltip Third"></ToolTip>
</ToolTipService.ToolTip>
</ContentControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGridTextColumn.HeaderStyle>
</data:DataGridTextColumn>
</data:DataGrid.Columns>
</data:DataGrid>
</Grid>
where Custdetails.. is something like this..
class Customer
{
public string LName { set; get; }
public string FName { set; get; }
public string City { set; get; }
}
DataBinding...
List<Customer> customers = new List<Customer>
{
new Customer { LName="Alan", FName="Ameen", City="New York" },
new Customer { LName="Forgeard", FName="Steven", City="Mumbai" },
new Customer { LName="Angur", FName="Paul", City="São Paulo" }
};
dgCustDetails.ItemsSource = customers;
This would display the header tooltips... To make it dynamic.. Replace the ToolTip Content with Binding & the desired value...

Categories