I have a main window viewmodel that is creating an instance of the usercontrol, and in a specific function, setting a property of the usercontrol via code i.e.
// Set Button Width Height
var potentialHeight = (SelectorUCViewModel.GridHeight /
SelectorUCViewModel.ColumnCount) - (SelectorUCViewModel.ButtonMarginToUse * 2);
var potentialWidth = (SelectorUCViewModel.GridWidth /
SelectorUCViewModel.RowCount) - (SelectorUCViewModel.ButtonMarginToUse * 2);
SelectorUCViewModel.ButtonHeightWidth = (potentialHeight < potentialWidth) ? potentialHeight : potentialWidth;
When this is called, the properties in the usercontrol change (you can see the setter is called). However, on the UI it looks like nothing has happened (margin is not there... toggle buttons are not there...)
I don't understand why....
An extract of XAML of my viewmodel has the following:
<DockPanel LastChildFill="False">
<views:SelectorUC x:Name="UC" DataContext="{Binding SelectorUCViewModel}" />
<Border DockPanel.Dock="Right" Width="160" Margin="10 0 10 0">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
Then the UserControl has the following XAML
<UserControl x:Class="SelectorUC"
x:Name="UC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Selector"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Name="mainGrid" DockPanel.Dock="Left" MouseDown="MainGrid_MouseDown" MouseUp="MainGrid_MouseUp" MouseMove="MainGrid_MouseMove" Background="Transparent"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
MinWidth="{Binding GridWidth}" Height="{Binding GridHeight}">
<ItemsControl x:Name="objItemControl" ItemsSource="{Binding ObjCompositeCollection}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
<Setter Property="Grid.Column" Value="{Binding Column}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid DockPanel.Dock="Top" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Name="grid" Grid.Row="1"
Width="{Binding MinWidth, ElementName=mainGrid}"
Height="{Binding Height, ElementName=mainGrid}"
GridHelper.RowCount="{Binding RowCount}"
GridHelper.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type ObjA}">
<ToggleButton Content="{Binding Id}"
Tag="{Binding Id}"
IsChecked="{Binding IsSelected}"
Height="{Binding Path=ButtonHeightWidth, ElementName=UC}"
Width="{Binding Path=ButtonHeightWidth, ElementName=UC}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="{Binding Path=ButtonMarginToUse, ElementName=UC}"
Padding="2"
PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown"
PreviewMouseLeftButtonUp="Button_PreviewMouseLeftButtonUp"
MouseEnter="Button_MouseEnter"
MouseLeave="Button_MouseLeave"
Style="{StaticResource ObjButton}">
</ToggleButton>
</DataTemplate>
<DataTemplate DataType="{x:Type GridLabeller}">
<TextBlock Text="{Binding HeaderName}" Style="{StaticResource GridHeaders}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
<Canvas>
<Rectangle x:Name="selectionBox" Visibility="Collapsed" Stroke="{StaticResource ResourceKey=SecondaryThemeColour}" StrokeThickness="2" StrokeDashArray="2,1"/>
</Canvas>
</Grid>
</UserControl>
And the properties that are resetting in the UC class
private int buttonHeightWidth;
public int ButtonHeightWidth
{
get { return buttonHeightWidth; }
set
{
buttonHeightWidth = value;
OnPropertyChanged(nameof(ButtonHeightWidth));
}
}
private int buttonMarginToUse = 2;
public int ButtonMarginToUse
{
get { return buttonMarginToUse; }
set
{
buttonMarginToUse = value;
OnPropertyChanged(nameof(ButtonMarginToUse));
}
}
The mainwindow view sets the datacontext of the mainwindowviewmodel
public MainWindow()
{
InitializeComponent();
viewModel = new MainWindowViewModel(model);
DataContext = viewModel;
SourceInitialized += Window1_SourceInitialized;
}
I genuinely can't understand what is wrong - there is no binding error, but since shifting my code from viewmodel to usercontrol (was trying to abstract as much as i can from vm) things are not working properly...
Related
I using code from a tutorial to develop a GUI with Tab Menu in WPF. Here, the buttons act as the Tab Menu header and every button is linked with an id. Based on the user selection, the id changes which in turn triggers what is displayed when a particular button is clicked. I want to change the background of the button, if it is selected. How can this be done?
Following is the xaml code:
<StackPanel Background="WhiteSmoke">
<Grid Height="40">
<StackPanel HorizontalAlignment="Left" Margin="20 0">
<ComboBox FontSize="15" Width="50" Foreground="#FFA2A2A2" SelectedIndex="0" VerticalAlignment="Center">
<ComboBoxItem Content="EN"/>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="20 0">
<Button Content="FAQ" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FFA2A2A2" FontSize="15" FontWeight="Bold" VerticalAlignment="Center"/>
<Button Content="CONTACT" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FFA2A2A2" FontSize="15" FontWeight="Bold" VerticalAlignment="Center"/>
<Button Content="ORDER STATUS" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FFA2A2A2" FontSize="15" FontWeight="Bold" VerticalAlignment="Center"/>
<Button Content="MY ACCOUNT" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FFA2A2A2" FontSize="15" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
<Grid Height="100">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="10 0">
<Button x:Name="b1" Uid="0" Width="150" Content="HOME" Height="50" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FF2196F3" Click="Button_Click"/>
<Button Uid="1" Width="150" Content="SHOP" Height="50" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FF2196F3" Click="Button_Click"/>
<Button Uid="2" Width="150" Content="BLOG" Height="50" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FF2196F3" Click="Button_Click"/>
<Button Uid="3" Width="150" Content="PAGES" Height="50" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FF2196F3" Click="Button_Click"/>
<Button Uid="4" Width="150" Content="PRODUCTS" Height="50" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FF2196F3" Click="Button_Click"/>
<Button Uid="5" Width="150" Content="BRANDS" Height="50" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FF2196F3" Click="Button_Click"/>
<Button Uid="6" Width="150" Content="GIFT CARDS" Height="50" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="#FF2196F3" Click="Button_Click"/>
</StackPanel>
<Grid x:Name="GridCursor" Width="150" Height="5" Background="#FF2196F3" HorizontalAlignment="Left" Margin="10 0"/>
</Grid>
<Grid x:Name="GridMain" Height="460" Background="Aquamarine">
</Grid>
</StackPanel>
Following is the code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int index = int.Parse(((Button)e.Source).Uid);
GridCursor.Margin = new Thickness(10 + (150 * index), 0, 0, 0);
switch (index)
{
case 0:
GridMain.Background = Brushes.Aquamarine;
b1.Background = Brushes.Yellow;
break;
case 1:
GridMain.Background = Brushes.Beige;
break;
case 2:
GridMain.Background = Brushes.CadetBlue;
break;
case 3:
GridMain.Background = Brushes.DarkBlue;
break;
case 4:
GridMain.Background = Brushes.Firebrick;
break;
case 5:
GridMain.Background = Brushes.Gainsboro;
break;
case 6:
GridMain.Background = Brushes.HotPink;
break;
}
}
I am able to change the background if the button is selected using b1.Background = Brushes.Yellow; . However, the background doesn't change to default when a different button is selected. Also, I am not able to set the background of the button (Uid =1) with which the GUI is launched.
I'm using the community toolkit mvvm for this sample below. I'm not sure this does exactly what you're asking but the binding and templating demonstrates the mvvm approach.
My mainwindow - no buttons.
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Choices}"
x:Name="lb"
>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="160">
<Grid.RowDefinitions>
<RowDefinition Height="22"/>
<RowDefinition Height="12"/>
</Grid.RowDefinitions>
<Rectangle Fill="Yellow">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsSelected}"
Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<TextBlock Text="{Binding Description}"
TextAlignment="Center"
/>
<Border Grid.Row="1"
CornerRadius="10"
>
<Border.Background>
<SolidColorBrush Color="{Binding BackgroundBrushName}"/>
</Border.Background>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Rectangle Grid.Row="1">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding SelectedItem.BackgroundBrushName, ElementName=lb}"/>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
Mainwindowviewmodel
public partial class MainWindowViewModel : ObservableObject
{
public List<Choice> Choices { get; set; } = new List<Choice>
{
new Choice{ Description = "Shop", BackgroundBrushName = "Red"},
new Choice{ Description = "Blog", BackgroundBrushName = "Blue"},
new Choice{ Description = "Products", BackgroundBrushName = "Green"}
};
}
Choice
public partial class Choice : ObservableObject
{
public string BackgroundBrushName { get; set; }
[ObservableProperty]
private string description = string.Empty;
}
None of those properties change but you should always implement inotifypropertychanged on any viewmodel and ObservableObject adds that. description does not change so it doesn't really need change notification but I thought it would be interesting to see the code generator creates a Description property for you.
The collection is templated out into UI. The "menu" is a horizontal listbox which has the selector behaviour so you can click one and it is selected.
The background for the selected choice is usually visibility collapsed but a datatrigger sets it to visible if it's in the selected item.
We could also bind selecteditem mode=twoway and set that to the first choice if we wanted an initial one....errm... chosen.
Hopefully this gives you a reasonably beginner friendly introduction to mvvm techniques.
I'm using LiveCharts in WPF to visualize the results of some analyses. The results of an analysis is added to a SeriesCollection and displayed in an CartesianChart. You can choose which type of series to use: LineSeries or ColumnSeries. The chosen type is then created and added to the SeriesCollection.
There's a custom mapper for selecting X and Y values from the ChartValues and a AxisFormatter for the X axis.
The charts are part of an Blacklight.Controls.Wpf.DragDockPanelHost. Each chart is an DragDockPanel with a style attached to it. The chart itself is a ContentControl with an TemplateSelector that returns the CartesianChart-XAML as a DataTemplate.
I've already tried to set the Fill or Stroke of the series or putting some ColumnSeries in there manually but that didn't help at all.
Filling of the SeriesCollection:
private SeriesCollection _Series;
public SeriesCollection Series
{
get { return _Series; }
set { SetProperty<SeriesCollection>(ref _Series, value); }
}
...
private void createDiagram()
{
if (this._Analysis!= null && this._Diagram != null)
{
this.Series.Clear();
foreach (KeyValuePair<state, Dictionary<DateTime, int>> kvp in this.Analysis.Execute())
{
Series series = Activator.CreateInstance(Diagram) as Series;
if (series != null)
{
series.Title = kvp.Key.name;
series.Values = new ChartValues<KeyValuePair<DateTime, int>>(kvp.Value);
this.Serien.Add(series);
}
}
}
}
Mapper and AxisFormatter:
CartesianMapper<KeyValuePair<DateTime, int>> mapper = Mappers.Xy<KeyValuePair<DateTime, int>>().X(kvp => ((DateTimeOffset)kvp.Key).ToUnixTimeSeconds()).Y(kvp => kvp.Value);
this.Series = new SeriesCollection(mapper);
this.XFormatter = value =>
{
return DateTimeOffset.FromUnixTimeSeconds((long)value).DateTime.ToString("dd.MM.yyyy HH:mm");
};
TemplateSelector:
public class DashboardElementTemplateSelector : DataTemplateSelector
{
public DataTemplate ListDashboardElementTemplate { get; set; }
public DataTemplate SingleValueDashboardElementTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is ListDashboardElementViewModel)
return this.ListDashboardElementTemplate;
else
return this.SingleValueDashboardElementTemplate;
}
}
XAML of DragDockPanelHost:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<DataTemplate x:Key="listElement">
<views:ListDashboardElementView/>
</DataTemplate>
<DataTemplate x:Key="singleValueElement">
<views:SingleValueDashboardElementView/>
</DataTemplate>
<tempselect:DashboardElementTemplateSelector x:Key="elementTempSelector"
ListDashboardElementTemplate="{StaticResource listElement}"
SingleValueDashboardElementTemplate="{StaticResource singleValueElement}"
/>
</ResourceDictionary>
<ResourceDictionary>
<conv:BooleanToVisibilityConverter x:Key="visCon"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<bl:DragDockPanelHost ItemsSource="{Binding Diagrams}" Grid.Row="1" Margin="20" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<bl:DragDockPanelHost.Style>
<Style TargetType="bl:DragDockPanelHost">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
</Canvas>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="bl:DragDockPanelHost">
<ItemsPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</bl:DragDockPanelHost.Style>
<bl:DragDockPanelHost.DefaultPanelStyle>
<Style TargetType="{x:Type bl:DragDockPanel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Margin="10">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Background="#00000000" Margin="-2" Padding="5" Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<WrapPanel>
<Image Width="20" x:Name="GripBarElement" Source="/Aisys.XStorage.Dashboard;component/Images/move.png" Grid.Column="0" Cursor="Hand" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding Name}" Grid.Column="0" FontSize="16" FontWeight="Bold" Margin="10,0,0,0"/>
</WrapPanel>
<WrapPanel HorizontalAlignment="Right" Grid.Column="2">
<Button Command="{Binding ExecuteCommand}" CommandParameter="{Binding}" Margin="5,0,5,0">
<Button.Template>
<ControlTemplate>
<Image Source="/Aisys.XStorage.Dashboard;component/Images/Refresh.png"/>
</ControlTemplate>
</Button.Template>
</Button>
<Button Width="20" Command="{Binding DataContext.RemoveDiagramCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type bl:DragDockPanelHost}}}" CommandParameter="{Binding}">
<Button.Template>
<ControlTemplate>
<Image Source="/Aisys.XStorage.Dashboard;component/Images/Remove.png"/>
</ControlTemplate>
</Button.Template>
</Button>
<Button Command="{Binding DataContext.ShowPropertiesCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type bl:DragDockPanelHost}}}" CommandParameter="{Binding}" Margin="5,0,5,0">
<Button.Template>
<ControlTemplate>
<Image Source="/Aisys.XStorage.Dashboard;component/Images/Preferences.png"/>
</ControlTemplate>
</Button.Template>
</Button>
<ToggleButton x:Name="MaximizeToggleButton" VerticalAlignment="Top" HorizontalAlignment="Right" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, Path=IsMaximized}" Margin="0,5,5,0" Width="25" Height="25" Cursor="Hand">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Image Source="/Aisys.XStorage.Dashboard;component/Images/Maximize.png" Margin="0,0,0,5"/>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</WrapPanel>
</Grid>
</Border>
<Separator VerticalAlignment="Bottom" Margin="0,0,0,0"/>
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource elementTempSelector}" Grid.Row="1" Margin="10"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</bl:DragDockPanelHost.DefaultPanelStyle>
</bl:DragDockPanelHost>
XAML of chart:
<Grid>
<lvc:CartesianChart Series="{Binding Series}" LegendLocation="Right" Name="chart">
<lvc:CartesianChart.AxisX>
<lvc:Axis Title="Zeit" LabelFormatter="{Binding XFormatter}">
</lvc:Axis>
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
</Grid>
If I'm choosing LineSeries, everything works fine. But when I'm using ColumnSeries nothing is shown. You can see, that the axis is redrawn and the separators move. The legend is also drawn, but there are no columns visible.
Any ideas why this is happening?
I had the same problem recently. Unfortunately, this doesn't seem to be documented anywhere but for some reason if you have too many data points for the size of the graph, then none of the columns will display. You can either try reducing the number of data points until it works (in my case 90 data points wouldn't display, but 30 would), or on ColumnSeries there is a property ColumnPadding that you can turn down to zero and see if that helps.
I've a TabControl like this:
<TabControl>
<TabItem Header="playing" HorizontalAlignment="Left" Width="150" Tag="Tab1">
<TabItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" ToolTip="playing" />
<Image Margin="10,0,0,0" Source="/logo.png" Height="25"/>
</StackPanel>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
...
In this TabControl I've three different TabItem, each tab item have a default image. My goal is to change the image TabItem where the user has positioned the mouse.
So in this case the TabItem 1 with ToolTip "playing" instead of logo.png should have logo2 when the mouse is over this tab item.
How can I do this?
Note: Please, note that I'm using mahapp, and I'm using a DataTemplate for keep the tooltip text without override the original style of mahapp tab item.
Try this:
XAML:
<Controls:MetroWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:local="clr-namespace:MahApps.Metro.Application21"
x:Class="MahApps.Metro.Application21.MainWindow"
BorderThickness="1"
BorderBrush="{DynamicResource AccentColorBrush}"
Icon="mahapps.metro.logo2.png"
Title="MainWindow"
Height="350"
Width="525">
<Controls:MetroWindow.Resources>
<DataTemplate x:Key="DataTemplate1">
<StackPanel x:Name="Panel1" Orientation="Horizontal">
<TextBlock Text="{Binding Text}" ToolTip="{Binding Text}" />
<Image x:Name="Image1" Source="{Binding Logo}" Margin="10,0,0,0" Height="25"/>
</StackPanel>
<DataTemplate.Triggers>
<Trigger SourceName="Panel1" Property="IsMouseOver" Value="true" >
<Setter TargetName="Image1" Property="Source" Value="logo4.png" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</Controls:MetroWindow.Resources>
<Controls:MetroWindow.DataContext>
<local:MyViewModel/>
</Controls:MetroWindow.DataContext>
<Grid>
<TabControl ItemsSource="{Binding Data}" ItemTemplate="{StaticResource DataTemplate1}">
</TabControl>
</Grid>
ViewModel:
public class MyViewModel
{
public ObservableCollection<MyData> Data { get; set; }
public MyViewModel()
{
Data = new ObservableCollection<MyData>
{
new MyData {Logo = "logo1.png", Text = "playing 1" },
new MyData {Logo = "logo2.png", Text = "playing 2" },
new MyData {Logo = "logo3.png", Text = "playing 3" }
};
}
}
I am creating custom phone book which reads phone book contacts and displays inside my application. So I am creating a long list selector and a listbox inside it.
My listbox will contain phone contact name and list of phone number with the check box under the particular name. I wrote an event trigger inside my listbox to make track of the checkbox is clicked or not.
PROBLEM : Event is not firing in the view model. I suspect since the listbox is present inside another list(long list selector), event is not firing.
Here is xaml code:
<phone:LongListSelector Grid.Row="3" LayoutMode="List" ItemsSource="{Binding PhoneBookDataSource}" IsGroupingEnabled="True" HideEmptyGroups="True">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" >
<TextBlock Text="{Binding PhoneContactName}" FontWeight="SemiBold" FontSize="36" Foreground="Green"></TextBlock>
<ListBox ItemsSource="{Binding LstPhoneContactNumber,Mode=TwoWay}" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding PhoneNumberCheckedStateChangeCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="480">
<TextBlock Text="{Binding PhoneNumberItem}" FontSize="25" HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="Gray"></TextBlock>
<CheckBox Foreground="Black" Background="Black" VerticalAlignment="Center" HorizontalAlignment="Right" IsChecked="{Binding IsPhoneNumberItemChecked,Mode=TwoWay}"></CheckBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
<phone:LongListSelector.GroupHeaderTemplate>
<DataTemplate>
<Border Background="Transparent" Padding="5">
<Border Background="{StaticResource PhoneAccentBrush}" BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2" Width="62"
Height="62" Margin="0,0,18,0" HorizontalAlignment="Left">
<TextBlock Text="{Binding Key}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="48" Padding="6"
FontFamily="{StaticResource PhoneFontFamilySemiLight}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</Border>
</DataTemplate>
</phone:LongListSelector.GroupHeaderTemplate>
<phone:LongListSelector.JumpListStyle>
<Style TargetType="phone:LongListSelector">
<Setter Property="GridCellSize" Value="113,113"/>
<Setter Property="LayoutMode" Value="Grid" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border Background="{Binding Converter={StaticResource BackgroundConverter}}" Width="113" Height="113" Margin="6" >
<TextBlock Text="{Binding Key}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="48" Padding="6"
Foreground="{Binding Converter={StaticResource ForegroundConverter}}" VerticalAlignment="Center"/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</phone:LongListSelector.JumpListStyle>
</phone:LongListSelector>
Here is my view model:
public DelegateCommand PhoneNumberCheckedStateChangeCommand { get; set; }
public DelegateCommand SendSMSCommand { get; set; }
public CustomPhoneBookViewModel(INavigationService nav, IDataService data, IAESEnDecrypt encrypt, IGeoLocationService geoLocation, IMessageBus msgBus, ISmartDispatcher smartDispatcher)
: base(nav, data, encrypt, geoLocation, msgBus, smartDispatcher)
{
IsProgressBarBusy = true;
PhoneContactsList = new ObservableCollection<PhoneBookEntity>();
PhoneBookDataSource = new ObservableCollection<LLSAlphaKeyGroup<PhoneBookEntity>>();
InitializeDelegateCommands();
GetDeviceResolution();
ReadPhoneBook();
}
private void OnPhoneNumberItemCheckedStateChangedCommand()
{
try
{
foreach (var parentItem in PhoneBookDataSource)
{
foreach (var childItem in parentItem)
{
foreach (var item in childItem.LstPhoneContactNumber)
{
if (item.IsPhoneNumberItemChecked)
IsSendSMSButtonEnabled = true;
return;
}
}
IsSendSMSButtonEnabled = false;
}
}
catch { }
finally
{
SendSMSCommand.RaiseCanExecuteChanged();
}
}
Any suggestions is appreciated!!
The easiest way of making the nested ListBox interaction bind to the global ViewModel (instead of to its own, nested DataContext) is to assign a unique name to the outmost LongListSelector:
<phone:LongListSelector x:Name="OuterList" Grid.Row="3" LayoutMode="List" ItemsSource="{Binding PhoneBookDataSource}" IsGroupingEnabled="True" HideEmptyGroups="True">
and explicitly bind the Command to this element's DataContext (which is the global ViewModel):
<i:InvokeCommandAction Command="{Binding ElementName=OuterList, Path=DataContext.PhoneNumberCheckedStateChangeCommand}" />
I have a custom control ListBox that I would like to adjust its size according to the size of its ItemsPanel. For the items panel, I have a custom control WrapPanel that arranges the items accordingly. As you can see in the screen shot, the ListBox prefers to size itself to its parent control or availableSize.
So then I tried to make a custom control Grid, that had an ItemsSource property that handed the items to its listbox. But that didn't work either. When the Grid would arrange, then the ListBox would Arrange, which would cause the Grid to Arrange and so on.
So my question is, how to I create a custom control that has an ItemsSource property and an ItemsPresenter that sizes itself according to the contents of its children??
You just have to set your ListBox HorizontalAlignment to Left, and VerticalAlignment to Top. This should do the trick.
Simple example :
MainWindow.xaml
<Window x:Class="ListBoxFitItemsPanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="400" Width="400">
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" Background="Red" HorizontalAlignment="Left" VerticalAlignment="Top" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Background="AntiqueWhite" Margin="5" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
</ListBox>
</Window>
EDIT : It also works in a Binding scenario :
MainWindow.xaml
<Window x:Class="ListBoxFitItemsPanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ListBoxFitItemsPanel"
Title="MainWindow" Height="400" Width="400">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Item}">
<Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Items}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" Background="Red" HorizontalAlignment="Left" VerticalAlignment="Top" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Background="AntiqueWhite" Margin="5" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Window>
MainWindow.xaml.cs
using System.Collections.Generic;
using System.Windows;
namespace ListBoxFitItemsPanel
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public IEnumerable<Item> Items
{
get
{
for (int i = 0; i < 9; i++)
{
yield return new Item();
}
}
}
}
public class Item { }
}
This is what I had to do to get the functionality I wanted. First I have to create my SquareWrapPanel. This is where the magic happens and arranges my items the way I want.
protected override Size ArrangeOverride(Size finalSize)
{
return ArrangeByChildCount(finalSize);
}
private Size ArrangeByChildCount(Size finalSize)
{
double childWidth = 0;
double childHeight = 0;
foreach (UIElement e in Children)
{
e.Measure(finalSize);
childWidth = e.DesiredSize.Width;
childHeight = e.DesiredSize.Height;
}
if (Children.Count > 0)
{
int square = (int)Math.Sqrt(Children.Count);
int rowCount = square + Children.Count % square;
int columnCount = square;
double height = rowCount * childHeight;
double width = columnCount * childWidth;
Size size = new Size(width, height);
base.ArrangeOverride(size);
return size;
}
else
{
return new Size(300, 300);
}
}
Then I created a custom panel that extended ItemsControl. That way I could bind a collection of items to it. There is nothing in the code behind, but here is the style I had to use.
<Style TargetType="{x:Type local:SquareItemsPanel}" BasedOn="{StaticResource {x:Type ItemsControl}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="4">
<Expander x:Name="exp" Header="View">
<local:SquareWrapPanel IsItemsHost="True"/>
</Expander>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>