I'm developing an App for Windows Phone 8.1. In that App I want to bind the Items of an ObservableCollection<DisruptionDisplayElement> to a ListView. DisruptionDisplayElement has a property named bool IsFavorite. In the ListView I want to hide all items, where IsFavorite is false.
If I do it by using ItemContainerStyle and set the Visibility-Property to collapsed by using a Converter, it is not working. If I define the Backgroundcolor the same way for testing, it works.
I can also hide the Grid, where everything of the ListViewItem is in, but in that case I still have the the decoration of the ListViewItem, that takes round about 50 pixels of space.
Here is what I've got:
XAML:
<Page
x:Class="myApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:myApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converter="using:myApp.Converter"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
>
<Page.Resources>
<converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConv"/>
</Page.Resources>
<Grid>
<Hub Header="{Binding CityName}"
SectionsInViewChanged="Hub_SectionsInViewChanged"
Grid.Row="1"
>
<HubSection Header="My Lines" Name="hubFavorites">
<DataTemplate>
<Grid Margin="0,-25,0,0">
<ListView
ItemsSource="{Binding DisruptionDisplayList}"
Grid.Row="1"
>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<!-- This seems not to work -->
<Setter Property="Visibility" Value="{Binding IsFavorite, Converter={StaticResource BoolToVisibilityConv}}"/>
<!-- For testing -->
<Setter Property="Background" Value="Aqua"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<!-- The Visibility-Property is just for testing as described -->
<Grid
Margin="0,0,0,10"
Visibility="{Binding IsFavorite, Converter={StaticResource BoolToVisibilityConv}}"
>
<!-- Content here -->
<TextBlock Text="{Binding Message}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</HubSection>
</Hub>
</Grid>
</Page>
The Converter:
namespace myApp.Converter
{
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string culture)
{
return (bool) value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string culture)
{
throw new NotImplementedException();
}
}
}
DisruptionDisplayElement:
public class DisruptionDisplayElement
{
public string Message { get; set; }
public bool IsFavorite { get; set; }
}
Code Behind:
namespace myApp
{
public sealed partial class MainPage
{
public MainPage()
{
InitializeComponent();
DataContext = new ViewModel;
}
}
}
My "ViewModel"-Class:
namespace myApp
{
public class ViewModel
{
public ObserverableCollection<DisruptionDisplayElement> DisruptionDisplayList {get;set;}
public ViewModel()
{
DisruptionDisplayList = new ObservableCollection<DisruptionDisplayElement>();
DisruptionDisplayList.Add(new DisruptionDisplayElement() { IsFavorite = true, Message = "Message 1"});
DisruptionDisplayList.Add(new DisruptionDisplayElement() { IsFavorite = false, Message = "Message 2" });
DisruptionDisplayList.Add(new DisruptionDisplayElement() { IsFavorite = true, Message = "Message 3" });
}
}
}
What can I do to hide the ListViewItem without wasting all the space for emtpy ListViewItems if I hide the grid inside?
Edit:
Advanced the Code providing
Without a good, minimal, complete code example, it's not possible to provide an actual code example that shows the correct technique in your exact scenario.
However, the basic answer is that you should be using ICollectionView to filter the visual presentation based on some property.
There are a variety of ways to go about doing this. One of the simplest is to apply the filtering to the default view for your data source. Such a view always exists, and if you are only binding your data source to a single visual object, or you want all visual objects that present that data source to be filtered in the same way, then getting and modifying this view is the correct approach.
As an example:
ICollectionView view = CollectionViewSource.GetDefaultView(DisruptionDisplayList);
view.Filter = item => ((MyClass)item).IsFavorite;
You would configure this view in your code-behind at the appropriate time, e.g. when the user indicates through whatever input mechanism you've provided that they want to show only the favorite items.
Note that this approach avoids altogether trying to use the DataTemplate as the mechanism for showing or hiding the items. Instead, the items are filtered before they ever reach the ListView object.
if you are looking for UWP, just set MinHeight Property to 0 (zero):
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="MinHeight" Value="0" />
</Style>
</ListView.ItemContainerStyle>
Related
I am currently having a class myCommand
class myCommand : INotifyCollectionChanged , INotifyPropertyChanged
{
public string Name { get; set; }
public ObservableCollection<myFile> subCommand { get; set; }
}
I have two TreeView items in my window.
tvCommandList which contains all available commands and tvFinalList to hold all selected commands.
I used contextmenu to copy the items from tvCommandList to tvFinalList;
In the mainWindow, I have two ObservableCollection items which are bound to the TreeViewItems.
ObservableCollection<myCommand> cmdlist = null;
ObservableCollection<myCommand> finallist = null;
These are bound to the TreeView in the XAML file.
<Grid>
<Grid.Resources>
<ResourceDictionary>
<Style x:Key="styleTemplate" TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsInitiallySelected, Mode=TwoWay}" />
</Style>
<HierarchicalDataTemplate DataType="{x:Type data:myCommand}"
ItemsSource="{Binding subCommand, Mode=TwoWay}">
<TextBlock Text="{Binding Name, Mode=TwoWay}" />
</HierarchicalDataTemplate>
</ResourceDictionary>
</Grid.Resources>
</Grid>
<TreeView x:Name="tvSendList" ItemsSource="{Binding}" DataContext="{Binding cmdlist}">
<TreeView x:Name="tvRecvList" ItemsSource="{Binding}" DataContext="{Binding finallist}">
I copy TreeViewItem from cmdlist to finallist and edit them for customsing data.
The issue I am facing here is that if I modify an item (Update the Name value) in the finallist, the cmdlist item also gets updated, which
I am not sure how to go about to resolve this.
I tried moving the ResourceDictionary to each TreeView specifically, but still facing the same issue
You should also clone, i.e. create a copy of, each individual myFile object when you create a copy of the collection, e.g.:
finalist = new ObservableCollection<myFile>(cmdlist.Select(x => new myFile()
{
Property1 = x.Property1,
Property2 = x.Property2
//copy all property values...
}));
I want to bind a TextBlock to an ObservableCollection<Log>. Each Log has different Type (enum), each Type should be resulting in different Run's Foreground (e.g. Error is Red, Success is Green).
I've done a quick read to these Q&A:
WPF and ObservableCollection<T>
In WPF how to define a Data template in case of enum?
WPF DataTemplate Binding depending on the type of a property
But my mind got stuck, because, I'm very new to WPF.
This is the class Log and enum Type:
public enum Type
{
Start = 1,
Stop = 0,
Info = 2,
Success = 4,
Error = 8
};
public class Log
{
public Type Type { get; set; }
public string Message { get; set; }
}
...and this is how I created the collection:
public partial class MainWindow : Window
{
ObservableCollection<Log> mLogCollection = new ObservableCollection<Log>();
public ObservableCollection<Log> LogCollection
{
get { return mLogCollection; }
}
public MainWindow()
{
DataContext = this;
mLogCollection.Add(new Log { Type = Log.Type.Error, Message = "Operation failed" });
mLogCollection.Add(new Log { Type = Log.Type.Success, Message = "Operation complete" });
}
How do I make everything like I want so it will be resulting in something like this?:
<TextBlock>
<Run Text="Operation failed" Foreground="Red"/>
<Run Text="Operation complete" Foreground="Green"/>
</TextBlock>
A balanced XAML and code behind solution is preferred rather than just full XAML.
I'm sorry if my explanation is not clear enough, I'm a bit sleepy right now.
Any help would be appreciated.
Here's the easiest way:
<StackPanel Orientation="Vertical">
<ItemsControl
ItemsSource="{Binding LogCollection}"
>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Log">
<TextBlock
Text="{Binding Message}"
x:Name="MessageText"
/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Type}" Value="Error">
<Setter TargetName="MessageText" Property="Foreground" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="Success">
<Setter TargetName="MessageText" Property="Foreground" Value="Green" />
</DataTrigger>
<!--
Etc. for the other log type values.
-->
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But that's pure XAML and you want code behind. #Evk suggested a value converter for the foreground type, and that's a reasonable way to do it.
XAML:
<ItemsControl
ItemsSource="{Binding LogCollection}"
>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Log">
<DataTemplate.Resources>
<local:LogTypeBrushConverter
x:Key="LogTypeBrush"
/>
</DataTemplate.Resources>
<TextBlock
Text="{Binding Message}"
Foreground="{Binding Type, Converter={StaticResource LogTypeBrush}}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
C#. You defined your enum as Log.Type which won't compile, because the Log class has a property named Type. So I renamed the enum to LogType.
public class LogTypeBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Color color = Colors.Black;
switch ((LogType)value)
{
case LogType.Start:
color = Colors.DodgerBlue;
break;
case LogType.Stop:
color = Colors.OrangeRed;
break;
case LogType.Info:
color = Colors.Blue;
break;
case LogType.Success:
color = Colors.Green;
break;
case LogType.Error:
color = Colors.Red;
break;
}
return new SolidColorBrush(color);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Incidentally, DataContext = this is a bad idea in general. On a Window it's relatively harmless, but try it on a UserControl and you start needing bizarre wokarounds. You can leave that out and bind to LogCollection like so:
ItemsSource="{Binding LogCollection, RelativeSource={RelativeSource AncestorType=Window}}"
At work I have several pages, each with buttons in the same places, and with the same properties. Each page also has minor differences. To that end, we created a userControl Template and put all the buttons in it, then applied that user control to all the pages. However, now it's rather hard to access the buttons and modify them from each page's xaml, because they are inside a UserControl on the page..... How do I elegantly access the buttons from each page?
What I've tried:
Currently, we bind to a bunch of dependency properties. I don't like this option because I have a lot of buttons, and need to control a lot of properties on those buttons. The result is hundreds of dependency properties, and a real mess to wade through when we need to change something.
Another method is to use styles. I like this method generally, but because these buttons are inside another control it becomes difficult to modify them, and the template would only be exactly right for one button, at one time.
Adam Kemp posted about letting the user just insert their own button here, and this is the method I'm currently trying to impliment / modify. Unfortunately, I don't have access to Xamarin.
Although the template is inserted when the code runs, the template is not updating the button correctly. If I put a breakpoint in the MyButton Setter, I can see that value is actually an empty button, rather than the one I assigned in my main window. How do I fix this?
Here's some simplified Code:
My Template UserControl's xaml:
<UserControl x:Class="TemplateCode.Template"
x:Name="TemplatePage"
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"
mc:Ignorable="d"
d:DesignHeight="350"
d:DesignWidth="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Background="DarkGray">
<Grid>
<Button x:Name="_button" Width="200" Height="100" Content="Template Button"/>
</Grid>
</UserControl>
My Template UserControl's Code Behind:
using System.Windows.Controls;
namespace TemplateCode
{
public partial class Template : UserControl
{
public static Button DefaultButton;
public Template()
{
InitializeComponent();
}
public Button MyButton
{
get
{
return _button;
}
set
{
_button = value; //I get here, but value is a blank button?!
// Eventually, I'd like to do something like:
// Foreach (property in value)
// {
// If( value.property != DefaultButton.property) )
// {
// _button.property = value.property;
// }
// }
// This way users only have to update some of the properties
}
}
}
}
And now the application where I want to use it:
<Window x:Class="TemplateCode.MainWindow"
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"
mc:Ignorable="d"
xmlns:templateCode="clr-namespace:TemplateCode"
Title="MainWindow"
Height="350"
Width="525"
Background="LimeGreen"
DataContext="{Binding RelativeSource={RelativeSource Self}}" >
<Grid>
<templateCode:Template>
<templateCode:Template.MyButton>
<Button Background="Yellow"
Content="Actual Button"
Width="200"
Height="100"/>
</templateCode:Template.MyButton>
</templateCode:Template>
</Grid>
</Window>
And Now the Code Behind:
Using System.Windows;
Namespace TemplateCode
{
Public partial class MainWindow : Window
{
Public MainWindow()
{
InitializeComponent();
}
}
}
Edit: While I want to remove unnecessary dependency properties in the template userControl, I'd still like to set bindings on the button's properties from the XAML.
rather than use many dependency properties, prefer style approach. Style contains every property available for a Button control.
I would create a DependencyProperty for each button style in UserControl.
public partial class TemplateUserControl : UserControl
{
public TemplateUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty FirstButtonStyleProperty =
DependencyProperty.Register("FirstButtonStyle", typeof (Style), typeof (TemplateUserControl));
public Style FirstButtonStyle
{
get { return (Style)GetValue(FirstButtonStyleProperty); }
set { SetValue(FirstButtonStyleProperty, value); }
}
public static readonly DependencyProperty SecondButtonStyleProperty =
DependencyProperty.Register("SecondButtonStyle", typeof (Style), typeof (TemplateUserControl));
public Style SecondButtonStyle
{
get { return (Style)GetValue(SecondButtonStyleProperty); }
set { SetValue(SecondButtonStyleProperty, value); }
}
}
and then modify xaml for buttons to pick these styles:
<UserControl x:Class="MyApp.TemplateUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="300"
Background="DarkGray">
<StackPanel>
<Button x:Name="_button" Width="200" Height="100"
Style="{Binding Path=FirstButtonStyle, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Button x:Name="_button2" Width="200" Height="100"
Style="{Binding Path=SecondButtonStyle, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
</UserControl>
now when buttons have to be customized, that can achieved by custom styles:
<StackPanel>
<StackPanel.Resources>
<!--common theme properties-->
<Style TargetType="Button" x:Key="TemplateButtonBase">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Foreground" Value="Blue"/>
</Style>
<!--unique settings of the 1st button-->
<!--uses common base style-->
<Style TargetType="Button" x:Key="BFirst" BasedOn="{StaticResource TemplateButtonBase}">
<Setter Property="Content" Value="1st"/>
</Style>
<Style TargetType="Button" x:Key="BSecond" BasedOn="{StaticResource TemplateButtonBase}">
<Setter Property="Content" Value="2nd"/>
</Style>
</StackPanel.Resources>
<myApp:TemplateUserControl FirstButtonStyle="{StaticResource BFirst}"
SecondButtonStyle="{StaticResource BSecond}"/>
</StackPanel>
You could register a Dependency Property Button on your UserControland handle the initialization in its PropertyChangedCallback.
Template.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Windows.Markup.Primitives;
namespace TemplateCode
{
public partial class Template : UserControl
{
public Template()
{
InitializeComponent();
}
public static readonly DependencyProperty ButtonProperty =
DependencyProperty.Register("Button", typeof(Button), typeof(Template),
new UIPropertyMetadata(new PropertyChangedCallback(ButtonChangedCallback)));
public Button Button
{
get { return (Button)GetValue(ButtonProperty); }
set { SetValue(ButtonProperty, value); }
}
public static List<DependencyProperty> GetDependencyProperties(Object element)
{
List<DependencyProperty> properties = new List<DependencyProperty>();
MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element);
if (markupObject != null)
{
foreach (MarkupProperty mp in markupObject.Properties)
{
if (mp.DependencyProperty != null)
{
properties.Add(mp.DependencyProperty);
}
}
}
return properties;
}
private static void ButtonChangedCallback(object sender, DependencyPropertyChangedEventArgs args)
{
// Get button defined by user in MainWindow
Button userButton = (Button)args.NewValue;
// Get template button in UserControl
UserControl template = (UserControl)sender;
Button templateButton = (Button)template.FindName("button");
// Get userButton props and change templateButton accordingly
List<DependencyProperty> properties = GetDependencyProperties(userButton);
foreach(DependencyProperty property in properties)
{
if (templateButton.GetValue(property) != userButton.GetValue(property))
{
templateButton.SetValue(property, userButton.GetValue(property));
}
}
}
}
}
Template.xaml
UserControl DataContext is inherited from parent, no need not to set it explicitly
<UserControl x:Class="TemplateCode.Template"
x:Name="TemplatePage"
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"
mc:Ignorable="d"
d:DesignHeight="350"
d:DesignWidth="525"
Background="DarkGray">
<Grid>
<Button x:Name="button" Width="200" Height="100" Content="Template Button"/>
</Grid>
</UserControl>
MainWindow.xaml
You were setting Button.Content instead of Button
<Window x:Class="TemplateCode.MainWindow"
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"
mc:Ignorable="d"
xmlns:templateCode="clr-namespace:TemplateCode"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Content="Actual Button"
Width="200"
Height="100"
/>
</Window.Resources>
<Grid>
<templateCode:Template Button="{StaticResource UserButton}"/>
</Grid>
</Window>
EDIT - Binding Button.Content
3 ways to do this:
1. Dependency Properties
By far the best method. Creating UserControl DP's for every property on the Button is certainly overkill, but for those you want bound to the ViewModel / MainWindow DataContext it makes sense.
Adding in Template.xaml.cs
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Template));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
Template.xaml
<UserControl x:Class="TemplateCode.Template"
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Button x:Name="button" Width="200" Height="100" Content="{Binding Text}"/>
</Grid>
</UserControl>
MainWindow.xaml
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Width="200"
Height="100"
/>
</Window.Resources>
<Grid>
<templateCode:Template
Button="{StaticResource UserButton}"
Text="{Binding DataContext.Txt,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Grid>
Or
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Content="Actual Button"
Width="200"
Height="100"
/>
</Window.Resources>
<Grid>
<templateCode:Template
Button="{StaticResource UserButton}"/>
</Grid>
Value precedence: UserButton Content > DP Text, so setting the Content in Resources wins.
2. Creating the Button in your ViewModel
MVVM purists won't like this, but you could use the Binding mark up instead of StaticResource.
MainWindow.xaml
<Grid>
<templateCode:Template
Button="{Binding DataContext.UserButton,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Grid>
3. Setting the binding in code
As you already noticed, a ViewModel prop (e.g. Txt) can't be referenced in Resources because of the order everything is initialized. You can still do it in code later, but it gets a bit messy with the error to prove.
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Window', AncestorLevel='1''.
BindingExpression:Path=DataContext.Txt; DataItem=null; target element
is 'Button' (Name=''); target property is 'Content' (type 'Object')
Note you need to define the full path on the Content property (setting DataContext on parent won't do).
MainWindow.xaml
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Width="200"
Height="100"
Content="{Binding DataContext.Txt,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
/>
</Window.Resources>
<Grid>
<templateCode:Template Button="{StaticResource UserButton}"/>
</Grid>
Template.xaml.cs
private static void ButtonChangedCallback(object sender, DependencyPropertyChangedEventArgs args)
{
// Get button defined by user in MainWindow
Button userButton = (Button)args.NewValue;
// Get template button in UserControl
UserControl template = (UserControl)sender;
Button templateButton = (Button)template.FindName("button");
// Get userButton props and change templateButton accordingly
List<DependencyProperty> properties = GetDependencyProperties(userButton);
foreach (DependencyProperty property in properties)
{
if (templateButton.GetValue(property) != userButton.GetValue(property))
templateButton.SetValue(property, userButton.GetValue(property));
}
// Set Content binding
BindingExpression bindingExpression = userButton.GetBindingExpression(Button.ContentProperty);
if (bindingExpression != null)
templateButton.SetBinding(Button.ContentProperty, bindingExpression.ParentBinding);
}
If you can group your changes to your buttons to one or multiple properties on your datacontext, you could work with DataTriggers:
<Button x:Name="TestButton">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsButtonEnabled}" Value="True">
<Setter TargetName="TestButton" Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
You can even use multiple conditions with MultiDataTriggers.
The main problem is that Template components are initialized before mainwindow components.I mean all properties of the button in mainwindow are set after the button in template class is initialized. Therefore, as you said value sets to null. All I want to say is about sequence of initializing objects.If you make a trick such a way as follows ;
public partial class Template : UserControl
{
private Button _btn ;
public Template()
{
}
public Button MyButton
{
get
{
return _button;
}
set
{
_btn = value;
_button = value;
}
}
protected override void OnInitialized(EventArgs e)
{
InitializeComponent();
base.OnInitialized(e);
this._button.Content = _btn.Content;
this._button.Background = _btn.Background;
this.Width = _btn.Width;
this.Height = _btn.Height;
}
}
It is going to work undoubtly.
Another Option based on #Funk's answer is to make a content control instead of a button on the template, then bind the content control's content to your ButtonProperty in the code behind:
on the template:
<ContentControl Content={Binding myButton} Width="200" Height="100"/>
in the template code behind:
public static readonly DependencyProperty myButtonProperty =
DependencyProperty.Register("Button", typeof(Button), typeof(Template),
new UIPropertyMetadata(new PropertyChangedCallback(ButtonChangedCallback)));
and then on the Main Window:
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Content="Actual Button"
/>
</Window.Resources>
<Grid>
<templateCode:Template myButton="{StaticResource UserButton}"/>
</Grid>
The nice thing about this is that Visual Studio is smart enough to show this code at design time, as well as having less code overall.
You can set things constant things (like location, font, and coloring) for your button either on the content control or in a default style, and then modify just the parts you need for you button.
One option is to simply start writing C# on the xaml page using < ![CDATA[ *** ]]>
In the Main Window.xaml you change to:
<templateCode:Template x:Name="test">
<x:Code><![CDATA[
Void OnStartup()
{
test.MyButton.Content="Actual Button";
test.MyButton.Background = new SolidColorBrush(Color.FromArgb(255,255,255,0));
}
]]>
</x:Code>
Then right after Initialize Object() you call OnStartup().
Although this does let you edit specific properties in the xaml, this is about the same as just writing the code in the code behind, where others expect it to be.
I've got some troubles with a custom control I need to create. I try to explain you my needs first
I need to have a combobox that permits to check more than one item at time (with checkbox) but I want it to be smart enought to bind to a specific type.
I've found some MultiSelectionComboBox but none reflects my need.
Btw my main problem is that I wish to have a generic class as
public class BaseClass<T> : BaseClass
{
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<T>), typeof(BaseClass<T>), new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(BaseClass<T>.OnItemsSourceChanged)));
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
int i = 0;
//MultiSelectComboBox control = (MultiSelectComboBox)d;
//control.DisplayInControl();
}
public IEnumerable<T> ItemsSource
{
get { return (IEnumerable<T>)GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
}
}
}
public class BaseClass : Control
{
}
and a more context specific item for example
public class MultiCurr : BaseClass<Currency>
{
static MultiCurr()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiCurr), new FrameworkPropertyMetadata(typeof(MultiCurr)));
}
}
In my App.xaml I've defined a resource as
<ResourceDictionary>
<Style TargetType="local:MultiCurr">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MultiCurr">
<ComboBox Width="120" Background="Red" Height="30" ItemsSource="{Binding ItemsSource}" DisplayMemberPath="Description" ></ComboBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
In my MainWindow I've created an object as
<Grid>
<local:MultiCurr x:Name="test" ItemsSource="{Binding Currencies}"></local:MultiCurr>
</Grid>
and the MainWindow.cs is defined as
public partial class MainWindow : Window, INotifyPropertyChanged
{
private IList currencies;
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var lst = new List<Currency>();
for (int i = 0; i < 10; i++)
{
var curr = new Currency
{
ID = i,
Description = string.Format("Currency_{0}", i)
};
lst.Add(curr);
}
Currencies = lst;
}
public IList<Currency> Currencies
{
get
{
return this.currencies;
}
set
{
this.currencies = value;
NotifyPropertyChanged("Currencies");
}
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And here's the result ...
I was wondering what am I doing wrong? is it possible what am I tring to achieve?
Thanks
UPDATE #1:
I've seen that the main problem is the datacontext of the custom usercontrol
<Application.Resources>
<ResourceDictionary>
<Style TargetType="local:MultiCurr">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MultiCurr">
<ComboBox Width="120" Background="Red" Height="30" ItemsSource="{Binding **Currencies**}" DisplayMemberPath="{Binding **DisplayMemeberPath**}" ></ComboBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
If I put ItemsSource as Currency (which is a property of the MainWindow) it shows.
If I put ItemsSource and DisplayMemberPath (which are defined in the BaseClass no.. how can I set the context of the usercontrol to itself?)
UPDATE #2
I've added a GoogleDrive link to the project here if anyone wants to try the solution
Thanks
Combobox is not suitable control for multiselection, because it has given behaviour, that when yo select item, Combobox closes itself. That's why Combobox doest not have SelectionMode property like ListBox. I think that ListBox inside expander is what you need.
Generic Types are not a way to go. WPF handles this different, better way. Take listbox as an example. If you bind listbox.itemssource to generic observable collection, and you try to define e.g ItemTemplate, you get full intellisense when writing bindings and warning if you bind to not existing property. http://visualstudiomagazine.com/articles/2014/03/01/~/media/ECG/visualstudiomagazine/Images/2014/03/Figure8.ashx WPF designer automatically recognizes type parameter of your observable collection. Of cousre you need to specify type of datacontext in your page by using something like this: d:DataContext="{d:DesignInstance search:AdvancedSearchPageViewModel}". However your control dont have to be and shouldn't be aware of type of items.
Following example demonstrates control that meets your requirements:
<Expander>
<Expander.Header>
<ItemsControl ItemsSource="{Binding ElementName=PART_ListBox, Path=SelectedItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Mode=OneWay}" />
<Run Text=";" />
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Expander.Header>
<Expander.Content>
<ListBox x:Name="PART_ListBox" SelectionMode="Multiple">
<ListBox.ItemsSource>
<x:Array Type="system:String">
<system:String>ABC</system:String>
<system:String>DEF</system:String>
<system:String>GHI</system:String>
<system:String>JKL</system:String>
</x:Array>
</ListBox.ItemsSource>
</ListBox>
</Expander.Content>
</Expander>
I reccomend you to create control derived from ListBox (not usercontrol).
I have hardcoded datatemplates, but you should expose them in your custom dependency properties and use TemplateBinding in you control template. Of course you need to modify expander so it looks like combobox and ListBoxItem style so it looks like CheckBox, but it is ease.
I have an unknown list of variables that I wish to bind to specific controls within a WPF application. Is there a way to bind a variable out of the list with a specific name?
Here is a code example of what I am trying to do.
C#
public class Variable {
public string Name {get;set;}
}
public class VariableViewModel : INotifyPropertyChanged {
Variable _variable;
public Variable Variable {
get {
return(_variable);
}
set {
_variable = value;
if(PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs("Variable"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class VariableListViewModel {
public ObservableCollection<VariableViewModel> VariableList { get; set; }
public VariableListViewModel() {
VariableList = new ObservableCollection<VariableViewModel>();
var variableViewModel = new VariableViewModel {
Variable = new Variable { Name = "my_variable_name" }
};
VariableList.Add(variableViewModel);
}
}
WPF:
<Window>
<Window.DataContext>
<local:VariableListViewModel />
</Window.DataContext>
<StackPanel>
<Label Content="{Binding Path=Name, ElementName=my_variable_name}" />
</StackPanel>
</Window>
The label is clearly wrong here. My question is whether or not what I am trying to achieve is possible? I want to be able to display 'my_variable_name'.
-Stuart
You would normally use a ListView (or other ItemsControl), and bind its ItemsSource to your collection (VariableList).
This will cause each item (a VariableViewModel) to get displayed. You'd then use a DataTemplate to cause the VariableViewModel to display as a Label bound to Variable.Name.
You could use a ValueConverter and use its ConverterParameter to pass in the name of the variable:
public object ConvertTo(object value, Type targetType, object parameter, CultureInfo culture)
{
var variableList = (IEnumerable<Variable>)value;
var variableName = parameter != null ? parameter.ToString() : string.Empty;
return variableList.FirstOrDefault(v=>v.Name == variableName);
}
You could then use that converter in XAML as follows:`
<Label Content="{Binding Path=ListOfVariables,
Converter={StaticResource VariableConverter},
ConverterParameter='VariableName'}" />
Your question contradicts itself.
On the one hand you say you have an unknown list of variables, on the other hand your View knows which variables it wants to bind to.
So, either you know which variables you want to bind to or you don't know it.
Scenario 1: You know which variables you want to bind to:
Create a property per Variable in your ViewModel
Scenario 2: You don't know which variables you want to bind to:
Bind your VariableList property to an ItemsControl in the View and provide a DataTemplate that can render a VariableViewModel.
BTW: Having a VariableViewModel seems to be superfluous. You could use Variable directly. Especially when all VariableViewModel does is returning the Variable inside.
Based on Reeds solution and comments, here is my final solution. I used the same model and view model, so I will only show the View.
<Window>
<Window.DataContext>
<local:VariableListViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="itemsControlDataTemplate">
<Label x:Name="label" Content="{Binding Variable.Name}">
<Label.Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<Trigger Property="Content" Value="my_variable_name">
<Setter Property="Visibility" Value="Visible" />
</Trigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ItemsControl ItemTemplate="{StaticResource itemsControlDataTemplate}" ItemsSource="{Binding Path=VariableList}" />
</StackPanel>
</Window>