I think title is not clear
I am working with WPF and creating custom Messages control. I have message User Control and the message user controls showing in custom Messages control[ListBox].
XAML code:
<ListBox x:Name="uiMessages" ItemsSource="{Binding Path=Messages}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<wpfMessages:MessageDisplay>
</wpfMessages:MessageDisplay>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Current view:
Expected:
Now list box item is showing from top to bottom and after red message there empty space. I will be good whem the 2 green messages will bi in bottom and red one in top. Expected is add messages from botton to top no from top to botton like now.
any Ideas?
Thanks in advance Jamaxack!
It is a little unclear but I think you want to change the ItemsPanel Layout.
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VerticalAlignment="Bottom"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Created custom WrapPanel that reverses Elements
XAML code:
<ListBox x:Name="uiMessages" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<wpf:ReverseWrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
Code behind:
public class ReverseWrapPanel : WrapPanel
{
public new Orientation Orientation { get { return base.Orientation; } set { base.Orientation = value; ResetAll(); } }
/// <summary>
/// The opposite of the Orientation property.
/// </summary>
Orientation FlipDirection { get { return Orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal; } }
public ReverseWrapPanel()
{
Initialized += ReverseWrapPanel_Initialized;
}
void ReverseWrapPanel_Initialized(object sender, System.EventArgs e)
{
this.Mirror(FlipDirection);
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
foreach (UIElement child in Children.OfType<UIElement>())
{
child.Mirror(FlipDirection);
}
}
void ResetAll()
{
this.Mirror(FlipDirection);
foreach (UIElement child in Children.OfType<UIElement>())
{
child.Mirror(FlipDirection);
}
}
}
Thanx Jamshed!
Related
I am creating a rudimentary Gantt chart as a visual representation of a schedule of events. To do this, I have an ItemsControl to render schedule line items in a StackPanel. Within that "parent" ItemsControl, I have another ItemsControl to render the Gantt chart view - basically just shapes. This looks like the following:
<ItemsControl ItemsSource="{Binding ScheduleLines}"
Grid.Row="1"
Height="100">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}"
Grid.Column="0"/>
<ItemsControl ItemsSource="{Binding Events}"
Grid.Column="1">
<ItemsPanelTemplate>
<components:ScheduleRowPanel/>
</ItemsPanelTemplate>
<ItemContainerTemplate>
<scheduleitems:ScheduleEventElement EventDate="{Binding EventDate}"
Status="{Binding Status}"/>
</ItemContainerTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The datacontext for this view has an ObservableCollection called ScheduleLines. Each item of ScheduleLines has another observable collection of Events.
Inside the child ItemsControl, I have a custom panel that arranges the events:
public class ScheduleRowPanel : Panel
{
//Scale and MinDate are dependency properties on this custom panel
protected override Size ArrangeOverride(Size finalSize)
{
foreach(UIElement child in Children)
{
ArrangeChild(child, MinDate, Scale, finalSize.Height);
}
return finalSize;
}
private void ArrangeChild(UIElement child, DateOnly minDate, double scale, double panelHeight)
{
if (child.GetType() == typeof(ScheduleEventElement))
{
ScheduleEventElement eventElement = (ScheduleEventElement)child;
DateOnly eventDate = eventElement.EventDate;
double xoffset = scale * (eventDate.DayNumber - minDate.DayNumber);
double yoffset = panelHeight / 2;
child.Arrange(new Rect(xoffset, yoffset, 50, 50));
}
else
{
throw new InvalidOperationException("Have not implemented any other type of schedule entity");
}
}
}
The panel arranges events from the following, that are rotated rectangles with a fill:
public class ScheduleEventElement : FrameworkElement
{
public DateOnly EventDate
{
get { return (DateOnly)GetValue(EventDateProperty); }
set { SetValue(EventDateProperty, value); }
}
// Using a DependencyProperty as the backing store for EventDate. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EventDateProperty =
DependencyProperty.Register("EventDate", typeof(DateOnly), typeof(ScheduleEventElement), new PropertyMetadata(DateOnly.FromDayNumber(1)));
public EventStatus Status
{
get { return (EventStatus)GetValue(StatusProperty); }
set { SetValue(StatusProperty, value); }
}
// Using a DependencyProperty as the backing store for Status. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StatusProperty =
DependencyProperty.Register("Status", typeof(EventStatus), typeof(ScheduleEventElement), new PropertyMetadata(EventStatus.Scheduled));
protected override void OnRender(DrawingContext drawingContext)
{
RectangleGeometry rectangle = new RectangleGeometry();
SolidColorBrush fill = new SolidColorBrush();
rectangle.Rect = new Rect(0, 0, 10, 0);
RotateTransform rotate = new RotateTransform();
rotate.CenterX = 0.5;
rotate.CenterY = 0.5;
rotate.Angle = 45;
rectangle.Transform = rotate;
switch (Status)
{
case EventStatus.Scheduled:
fill.Color = (Color)ColorConverter.ConvertFromString("#262626");
break;
case EventStatus.Early:
fill.Color = (Color)ColorConverter.ConvertFromString("#009d9a");
break;
case EventStatus.Late:
fill.Color = (Color)ColorConverter.ConvertFromString("#6929c4");
break;
}
drawingContext.DrawGeometry(fill, null, rectangle);
}
}
I am getting the following error when running the nested ItemControl:
I am understanding this as I need to modify the inner ItemControl to instead of having ItemsSource = {Binding Events} to have <ItemsControl> <ItemsControl.ItemsSource/>, but I am not sure how to specify the items source using that syntax. Is my understanding correct? If so, what would be the correct syntax for this?
Further, do I have a correct implementation of the ItemsContainerTemplate? Or should this be in the ItemsTemplate?
Thanks
you can't just write <ItemsPanelTemplate> and <ItemContainerTemplate> inside <ItemsControl> tag, you need to write them inside relveant property tags: <ItemsControl.ItemsPanel> and <ItemsControl.ItemTemplate>.
<ItemsControl ItemsSource="{Binding Events}"
Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<components:ScheduleRowPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<scheduleitems:ScheduleEventElement EventDate="{Binding EventDate}"
Status="{Binding Status}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I want to implement a horizontal ListBox, with multiple lines.
Using WrapPanel:
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
I get the following result:
But I am looking for this result:
How can I implement this?
Based on your comments, I made a UniformGrid which adjusts the number of rows/columns, whenever the window is resized.
MainWindow.xaml:
<Grid x:Name="Container">
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid x:Name="ItemsGrid" Loaded="ItemsGrid_Loaded" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
The names and events are important. They are used for adjusting the columns/rows in the code file.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private UniformGrid itemsGrid;
public MainWindow()
{
InitializeComponent();
}
private void ItemsGrid_Loaded(object sender, RoutedEventArgs e)
{
// Set reference and adjust columns/rows once the UniformGrid is loaded.
itemsGrid = sender as UniformGrid;
((UniformGrid)sender).Columns = (int)(Container.ActualWidth / 250);
((UniformGrid)sender).Rows = (int)(Container.ActualHeight / 75);
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
// Adjust number of columns/rows whenevner the window is resized.
if (itemsGrid != null)
{
itemsGrid.Columns = (int)(Container.ActualWidth / 250);
itemsGrid.Rows = (int)(Container.ActualHeight / 75);
}
}
}
This could definitely be cleaned up a bit, but it seems to do the job. There might be more elegant solutions, but I couldn't find any...
I want to implement a GridView which takes 3 items in a row, and if the number of items are 2 in last row, then the last row items should be aligned center instead of being left-aligned. Here are a couple of images to explain what I want to achieve.
Currently my implementation looks like
.
And this is what I want to achieve.
Any help would be appreciated.
There are many ways realizing the feature that you mentioned.
To summarize it, you need to inherit GridView and override MeasureOverride ArrangeOverride method to re-calculate each Rect of Panel's children. This way is complex. For more info you could refer to
XAML custom panels overview.
And you could also use PrepareContainerForItemOverride method to re-layout the item directly.
<local:VariableGrid
x:Name="MyGridView"
SelectionMode="Single"
IsSwipeEnabled="False">
<local:VariableGrid.ItemTemplate >
<DataTemplate>
<StackPanel BorderBrush="Red" BorderThickness="3" Height="200" Width="200" Margin="20">
</StackPanel>
</DataTemplate>
</local:VariableGrid.ItemTemplate>
<local:VariableGrid.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid
Orientation="Horizontal"
VerticalAlignment="Top"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"
MaximumRowsOrColumns="4">
</VariableSizedWrapGrid>
</ItemsPanelTemplate>
</local:VariableGrid.ItemsPanel>
</local:VariableGrid>
VariableGrid.cs
public sealed class VariableGrid : GridView
{
public VariableGrid()
{
this.DefaultStyleKey = typeof(VariableGrid);
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
var list = this.ItemsSource as List<string>;
var griditem = element as GridViewItem;
for (var t = ((list.Count - list.Count % 4)); t < list.Count; t++)
{
if (item as string == list[t])
{
if (griditem != null)
{
VariableSizedWrapGrid.SetColumnSpan(griditem, 2);
}
}
}
base.PrepareContainerForItemOverride(element, item);
}
}
However, this simple way can not fit all the scenario.
I have a WPF control defined as follows:
<ItemsControl ItemsSource="{Binding DutyValueBinders}" IsEnabled="{Binding Enabled}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, TargetNullValue=''}" Width="50"></TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This produces a grid, looking like this:
So although there's only one text box defined in the XAML, the user sees a grid.
What I want to do is, after the user has typed a value and pressed Enter or Return, focus should pass to the next cell and so on until the end of the grid is reached. I tried putting this routine into the code behind (copied from another thread on Stackoverflow):
private void UserControl_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Enter || (!e.IsToggled && sender is Button)) return;
var request = new TraversalRequest(FocusNavigationDirection.Next);
var keyboardFocus = Keyboard.FocusedElement as UIElement;
if (keyboardFocus == null) return;
keyboardFocus.MoveFocus(request);
e.Handled = true;
}
and modified the header of the XAML as follows:
<UserControl ...
(Other references)
...
d:DesignHeight="300" d:DesignWidth="300" PreviewKeyDown="UserControl_PreviewKeyDown">
The trouble is, as there's only one text box in the XAML definition, there's no tabindex order for it to follow, and so when you press Enter the cursor just vanishes.
Is there any way to do this, short of scrapping the XAML entirely and starting again with a different type of control?
How about handling the PreviewKeyDown event for the ItemsControl like this?:
<ItemsControl ItemsSource="{Binding DutyValueBinders}" IsEnabled="{Binding Enabled}"
PreviewKeyDown="ItemsControl_PreviewKeyDown">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, TargetNullValue=''}" Width="50"></TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
private void ItemsControl_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TextBox textBox = Keyboard.FocusedElement as TextBox;
if (textBox != null)
{
textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
I have a Datatemplete for List-box Item in which I have a Grid with two columns using WPF. In the first column I want to put few customized controls(Buttons) dynamically using C# in code behind. I don't know how to start and from where should I start, can anybody please help me with some great inputs and examples. Any answer will be greatly appreciate.
Thanks in advance.
XAML code:
<ListBox x:Name="ListBoxItem"
Grid.Row="1"
SelectionMode="Extended"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
FocusVisualStyle="{x:Null}"
KeyboardNavigation.IsTabStop="False"
Background="DarkGray"
ItemsSource="{Binding}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="ListContent"
IsItemsHost="True"
Width="500"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="True"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<StackPanel DockPanel.Dock="Left"
Width="30"
Height="{Binding Height}">
<--Here I want to put few customize buttons in code behind-->
</StackPanel>
<Image x:Name="MainPage"
Stretch="UniformToFill"
Source="{Binding ImagePath}"
Height="{Binding Height}"
Width="{Binding Width}"/>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
You specified wanting to use code behind, so it would look something like this:
XAML:
<StackPanel Initialized="StackPanel_Initialized" .. />
Code behind:
using MyNamespace;
private void StackPanel_Initialized(object sender, EventArgs e)
{
MyControl newItem = new MyControl();
// Set any other properties
StackPanel parent = sender as StackPanel;
parent.Children.Add(newItem);
}
If you are looking for adding Controls inside a the First column of your grid then put a Panel inside the first column and in code behind add controls as child to that Panel. So as you mentioned in above that you are using DataTemplete then I would like to say that you can access that Panel something like:
Put the below codes inside the event where you wnt to add the controls.
ListBoxItem item = (ListBoxItem)(this.lst.ItemContainerGenerator.ContainerFromIndex(i));
ContentPresenter presenter = FindVisualChild<ContentPresenter>(item);
DataTemplate template = presenter.ContentTemplate;
StackPanel stack = (StackPanel)template.FindName("FirstColumn Panel Name", presenter);
and then call the below method:
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}