WPF combination between grid and stackpanel - c#

I am currently trying to find/implement a WPF control, which is a combination of a Grid and a StackPanel. That means, I like to have two columns with several items. An item is another control (e.g. single Label, TextBox with label in a panel, ...).
If an item is collapsed, the empty space should be filled with the next item, means I have always as less space used in the control as possible (without gaps between the single items).
I attached two images how it should look like.
Initial:
Item4 is collapsed (notice the shift of following up items):
Does anybody have an idea or experience how to do something like that?

you can use a ListView that uses a StackPanel or a DockPanel as ItemTemplate.
This is an example (simplified) of what I use to display an observable collection of objects in a list with Grid and DockPannel:
<ListView x:Name="lbxDroppedDatas" SelectionMode="Single" AllowDrop="True" BorderThickness="0" Padding="0" Margin="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionChanged="lbxDroppedDatas_SelectionChanged" PreviewKeyDown="lbxDroppedDatas_PreviewKeyDown" PreviewMouseRightButtonDown="lbxDroppedDatas_PreviewMouseRightButtonDown" >
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Grid.Row="0" Width="{Binding Path=ActualWidth, ElementName=Container}" Height="40" Margin="1" LastChildFill="True">
<CheckBox DockPanel.Dock="Left" IsChecked="{Binding IsChecked}" VerticalAlignment="Center" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
<Label Content="{Binding Name}" Foreground="{Binding LineBrush}" FontWeight="Bold" FontSize="12" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="0" MouseEnter="Label_MouseEnter" MouseLeave="Label_MouseLeave"/>
<TextBox DockPanel.Dock="Right" Width="60" MaxLength="14" Text="{Binding CurrentValue, StringFormat=N}" Margin="0,0,33,0" Background="Transparent" Foreground="{Binding LineBrush}" BorderBrush="{Binding LineBrush}" BorderThickness="1" VerticalAlignment="Center" HorizontalAlignment="Right" IsEnabled="False"/>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" VerticalAlignment="Stretch" Margin="0"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
You could bind the visibility of your list element to a collapsed or visible property.

I'd solve this problem in the following way:
start with creating a grid with two stack panels (the grid resides within a UserControl or any other placeholder):
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="LeftPanel" x:FieldModifier="private" Orientation="Vertical" />
<StackPanel x:Name="RightPanel" x:FieldModifier="private" Grid.Column="1" Orientation="Vertical" />
</Grid>
to control your items, create a list of items, preferably within the code of your UserControl:
private List<FrameworkElement> m_items = new List<FrameworkElement>();
for the items you don't want to be visible set the Visibility to Hidden or Collapsed.
you'd need some method that will select the visible items and place them alternating to the left and right panel. I've came up with some quick method that you need to call each time your list changes (an item is added or removed, visibility of an item is changed):
public void SortItems()
{
this.LeftPanel.Children.RemoveRange(0, this.LeftPanel.Children.Count);
this.RightPanel.Children.RemoveRange(0, this.RightPanel.Children.Count);
this.m_items.Where(i => i.Visibility == Visibility.Visible).ToList().ForEach((i) => { (this.LeftPanel.Children.Count == this.RightPanel.Children.Count ? this.LeftPanel : this.RightPanel).Children.Add(i); } );
}
The method simply removes all children of the stack panels and then traverses through the items' list choosing only those which are Visible and adds one to the left panel if both panels have the same amount of items in them and to the right panel otherwise.
If you need some other way of selecting into which panel the following item should be added (e.g. ActualHeight) simply change the condition that is selecting the panels.
If you want to make this method more elegant you can add some events or dependency properties that will call SortItems automatically. Anyway, it's a good start.

Thank you very much for all the different solutions. Finally I solved it with the suggestion from Sinatr. So I created my own panel and override the ArrangeOverride:
protected override Size ArrangeOverride(Size finalSize)
{
List<UIElement> visibleItems = new List<UIElement>();
double xColumn1 = 0;
double xColumn2 = (finalSize.Width / 2) + (WIDTH_COLUMN_SEPERATOR / 2);
double y = 0;
double columnWidth = (finalSize.Width - WIDTH_COLUMN_SEPERATOR) / 2;
for (int i = 0; i < InternalChildren.Count; i++)
{
UIElement child = InternalChildren[i];
if (child.Visibility != Visibility.Collapsed)
{
visibleItems.Add(child);
}
}
for (int i = 0; i < visibleItems.Count; i++)
{
if (i >= (visibleItems.Count - 1))
{
visibleItems[i].Arrange(new Rect(xColumn1, y, columnWidth, visibleItems[i].DesiredSize.Height));
}
else
{
UIElement leftItem = visibleItems[i];
UIElement rightItem = visibleItems[i + 1];
double rowHeight = leftItem.DesiredSize.Height > rightItem.DesiredSize.Height ? leftItem.DesiredSize.Height : rightItem.DesiredSize.Height;
leftItem.Arrange(new Rect(xColumn1, y, columnWidth, rowHeight));
rightItem.Arrange(new Rect(xColumn2, y, columnWidth, rowHeight));
y += rowHeight;
i++;
}
}
return finalSize;
}
I also created my own UserControl for the Items:
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label x:Name="captionLabel" Grid.Row="0" Content="{Binding Caption}"/>
<ContentPresenter x:Name="inputMask" Grid.Row="1" Content="{Binding InputMask, ElementName=userControlBRAIN2AttributePanelItem}" />
</Grid>
So I get te following result (from my test application):
enter image description here
with collapsed item 3:
enter image description here

I'm surprised that no one sugested the use of an UniformGrid.
All you have to do is declar an UniformGrid, set Columns to 2 and add your items. Items that are collapsed should have its Visibility set to Collapsed (duh).
Here's an example:
<UniformGrid Columns="2"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock Text="Item 1 " />
<TextBlock Text="Item 2 " />
<TextBlock Text="Item 3 " />
<TextBlock Text="Item 4 " />
</UniformGrid>
Will produce the following result:
And when the Visibility of the second TextBlock is set to Collapsed, here's what it looks like:

Related

How can I remove selected row from a grid control

I have created a Grid and adding rows dynamically. And I have a remove button in every row. On click of that remove button I want to remove that particular row.
I am quite new to WPF, can some one help me out to achieve this.
Thanks.
Xaml code:
<Grid Grid.Column="2" Visibility="Collapsed" Name="operationalGrid">
<Button Height="25" Name="btnRemove" Width="24" Style="{DynamicResource ButtonStyle}" Grid.Column="4" Click="btnRemove_Click">
<materialDesign:PackIcon Background="Transparent" Height="25" Width="30" Cursor="Hand" ToolTip="Remove" Kind="Minus" HorizontalAlignment="Center" Foreground="White" VerticalAlignment="Top" Margin="0,0,0,0">
</materialDesign:PackIcon>
</Button>
</Grid>
c# code:
Adding rows:
RowDefinition row = new RowDefinition();
row.Height = GridLength.Auto;
if (ucDynamicControls != null)
ucDynamicControls.btnToggle.Visibility = ucDynamicControls.btnToggle.Visibility = Visibility.Visible;
ucDynamicControls = new ucControls();
grdContols.RowDefinitions.Add(row);
Grid.SetRow(ucDynamicControls, controlCount++);
grdContols.Children.Add(ucDynamicControls);
Instead of using row definitions, you may use ItemsControl.
As example:
First define your ItemTemplate
<ItemsControl Name="icTodoList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Define your ItemsPanel
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Full tutorial:
https://www.wpf-tutorial.com/list-controls/itemscontrol/
You have come up with a very complex, one might say, perverse way of accomplishing the task.
A typical implementation is to create a container element with data. Then the collection with these elements is passed to ItemsControl. And he already visual each element as it should.
In this implementation: deleting a row is removing an item from the collection.
In your implementation, you need to parse the Visual Element Tree.
It is possible, but difficult.
The button, as I understand it, is inside the Grid row.
This Grid is inside ucDynamicControls.
And the outer Grid row already contains this ucDynamicControls.
If I understood correctly, then in the button event it is necessary to go up from the button to the Grid containing ucDynamicControls.
In this Grid, remove the ucDynamicControls element from the Children collection.
And the line (in which it) is to collapse: Heigh = 0.
And it's not clear why you chose Grid, and StackPanel for the ucDynamicControls collection?
If you use a StackPanel, then it will be possible not to set the line number and for it will be enough just to remove the desired ucDynamicControls from the collection.
An example implementation (if I understand your code correctly):
private void btnRemove_Click(object sender, RoutedEventArgs e)
{
if (!(sender is Button button))
return;
DependencyObject child = button;
DependencyObject parent;
while (!((parent = LogicalTreeHelper.GetParent(child)) is UcDynamicControls) && parent != null)
child = parent;
if (parent == null)
return;
UcDynamicControls ucDynamicControls = (UcDynamicControls)parent;
int row = Grid.GetRow(ucDynamicControls);
Grid grid = LogicalTreeHelper.GetParent(ucDynamicControls) as Grid;
if (grid == null || grid.RowDefinitions.Count <= row)
return;
grid.RowDefinitions[row].Height = new GridLength(0);;
grid.Children.Remove(ucDynamicControls);
}

WPF C# Get array position of grid button from Click Event

I have a window that I'm basically building ghetto minesweeper in. I have a grid that I feed a jagged array into, set up so that it will adapt to any change in the size of the array (no hard set values or rows/columns). Over top of that, I have a grid of blank buttons that simply adapts in size to the grid below.
When you click a button, it hides revealing the value under it, and I need some way to return the position of the button clicked, so I can compare against the original jagged array to find out whether not the item was a bomb. (this would also help me for doing a fill action on empty tiles). But given how I have it set up, the Grid.GetRow or Grid.GetColumn just return 0's.
Does anyone know how I can get the array position (preferably row and column) from the setup that I have?
XAML Below, the C# click events follow it.
<Window x:Class="MinesweeperWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Minesweeper" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto">
<Window.Resources>
<DataTemplate x:Key="Buttons_Template">
<Button Content=""
Height="20"
Width="20"
Margin="0,0,0,0"
Visibility="Visible"
Click="ButtonClick"
MouseRightButtonUp="RightClick"/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_2">
<TextBlock Text="{Binding}"
Height="20"
Width="20"
Margin="0,0,0,0"
FontFamily="Rockwell"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Padding="5"/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
<DataTemplate x:Key="Buttons_Template2">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource Buttons_Template}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<TextBlock x:Name="RemainingMines" HorizontalAlignment="Left" />
<TextBlock x:Name="Difficulty" HorizontalAlignment="Center" />
<TextBlock x:Name="Timer" HorizontalAlignment="Right" />
</Grid>
<Grid Name="ResetButton" Grid.Row="1">
<Button Name="Reset" Content="Reset"/>
</Grid>
<Grid Name="GridBoard" ShowGridLines="True" Grid.Row="2">
<ItemsControl x:Name="GridItems" ItemTemplate="{DynamicResource DataTemplate_1}"/>
</Grid>
<Grid Name="ButtonsBoard" ShowGridLines="True" Grid.Row="2">
<ItemsControl x:Name="ButtonItems" ItemTemplate="{DynamicResource Buttons_Template2}"/>
</Grid>
</Grid>
</Window>
Click Methods in C#
private void ButtonClick(object sender, RoutedEventArgs e)
{
int col = Grid.GetColumn((Button)sender); //this just returns 0
int row = Grid.GetRow((Button)sender); //this just returns 0
Button source = e.Source as Button;
source.Visibility = Visibility.Hidden;
Console.WriteLine("L: {0} x {1}", col, row); //prints L: 0 x 0
}
private void RightClick(object sender, RoutedEventArgs e)
{
int col = Grid.GetColumn((Button)sender); //this just returns 0
int row = Grid.GetRow((Button)sender); //this just returns 0
Button source = e.Source as Button;
if(source.Content.Equals(""))
{
source.Content = "\u2691";
}
else
{
source.Content = "";
}
Console.WriteLine("R: {0} x {1}", col, row); //prints R: 0 x 0
}
Any help would be appreciated on this.
You need to use appropriate control for buttons to host. In your xaml you are using items control inside Grid. But Items control do not have row and column index. Thats why you are not able to get the index.
Use UniformGrid or some relevant control.
You can refer this article
https://stackoverflow.com/a/13588066/5273015
Will help you a lot in other assignments as well

Is there any control for auto resize items inside

is there any control that can auto resize items inside. I need to contain textblocks inside ItemsControl only in one row, so if total items width larger then container.Width, it will change each item's width. UWP
<ItemsControl Grid.Column="1"
ItemsSource="{Binding Path=NavigationHistory, ElementName=Main, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="BroadComb"
MaxHeight="24"
Margin="10,0,0,0" VerticalAlignment="Center" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<HyperlinkButton Command="{Binding Path=NavigateCommand, ElementName=Main}" CommandParameter="{Binding}" Margin="10,0,0,0" Foreground="{ThemeResource AppAccentForegroundLowBrush}" >
<HyperlinkButton.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
TextTrimming="None"
Text="{Binding Path=DisplayName}"
FontSize="10"
FontWeight="Bold"
Foreground="{ThemeResource AppAccentForegroundLowBrush}">
<i:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding Path=CanBeTrim}" Value="True">
<core:ChangePropertyAction PropertyName="TextTrimming" Value="WordEllipsis"/>
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding Path=CanBeTrim}" Value="False">
<core:ChangePropertyAction PropertyName="TextTrimming" Value="None"/>
</core:DataTriggerBehavior>
</i:Interaction.Behaviors>
</TextBlock>
<FontIcon Margin="10,0,0,0" HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="ms-appx:/Assets/Fonts/MyBook-Regular.ttf#MyBook"
FontSize="10"
Glyph="2" Foreground="{ThemeResource AppAccentForegroundLowBrush}" />
</StackPanel>
</DataTemplate>
</HyperlinkButton.ContentTemplate>
</HyperlinkButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
One possibility is to use a Grid with your desired controls in it's columns set to HorizontalAlignment="Stretch":
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="abc" HorizontalAlignment="Stretch" />
<TextBlock Grid.Column="1" Text="xyz" HorizontalAlignment="Stretch" />
</Grid>
This will stretch both controls over the maximum available width according to the Grid's container and make them smaller or bigger if the available space changes.
Most of the time, you will only want to set one column to fill the available space (Width="*") and set the other columns to a fixed or relative width. You can experiment with those settings.
Else we would need a more exact specification of what you want to achieve.
is there any control that can auto resize items inside. I need to contain textblocks inside ItemsControl only in one row, so if total items width larger then container.Width, it will change each item's width. UWP
What you need is to change the default ItemsPanel of GridView. You can create your own Custom Panel to let the texts be arranged in only one row:
Create your own custom Panel OneRowPanel.cs:
public class OneRowPanel:Panel
{
double itemHeight=0;
protected override Size MeasureOverride(Size availableSize)
{
int count = Children.Count;
foreach (FrameworkElement child in Children)
{
child.Measure(new Size(availableSize.Width/count,availableSize.Height));
if (child.DesiredSize.Height > itemHeight)
{
itemHeight = child.DesiredSize.Height;
};
}
// return the size available to the whole panel
return new Size(availableSize.Width, itemHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
// Get the collection of children
UIElementCollection mychildren = Children;
// Get total number of children
int count = mychildren.Count;
// Arrange children
int i;
for (i = 0; i < count; i++)
{
//get the item Origin Point
Point cellOrigin = new Point(finalSize.Width / count * i,0);
// Arrange child
// Get desired height and width. This will not be larger than 100x100 as set in MeasureOverride.
double dw = mychildren[i].DesiredSize.Width;
double dh = mychildren[i].DesiredSize.Height;
mychildren[i].Arrange(new Rect(cellOrigin.X, cellOrigin.Y, dw, dh));
}
// Return final size of the panel
return new Size(finalSize.Width, itemHeight);
}
}
Use it in your Xaml and set the TextBlock.TextTrimming to CharacterEllipsis:
<Page
x:Class="AutoResizeItemsSample.MainPage"
...
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="TextTemplate">
<TextBlock Text="{Binding Text}" TextTrimming="CharacterEllipsis">
</TextBlock>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Name="rootPanel">
<GridView Name="gridView" ItemTemplate="{StaticResource TextTemplate}" >
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<local:OneRowPanel ></local:OneRowPanel>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</StackPanel>
</Grid>
</Page>
Here is the Result:
Here is the complete demo:AutoResizeItemsSample.
Solve this easily with ListView. The ItemsPanel is already a StackPanel and the individual item will resize as needed. If you need the horizontal width of each item to be different, that's supported out of the box, too. I think the solution is to simply choose the ListView control.

How to set different Binding to each Array element? C# UWP

I managed to display my array in TextBlock - each [element] in separate row, but I would like to put each Array[index] element into separate TextBlock in the same Row, as long as it comes from same line, So:
A file is accessed
Lines are sorted
Lines are split into words
Each line is put on separate row
5. Each word from line is put into separate TextBlock but within the same row.
Here is my code:
public async void ReadFile()
{
var path = #"CPU.xls";
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
var file = await folder.GetFileAsync(path);
var readFile = await Windows.Storage.FileIO.ReadLinesAsync(file);
foreach (string line in readFile.OrderBy(line =>
{
int lineNo;
var success = int.TryParse(line.Split(';')[4], out lineNo);
if (success) return lineNo;
return int.MaxValue;
}))
{
string[] splitLines = line.Split(';');
for (int index = 0; index < splitLines.Length; index++)
{
itemsControl.Items.Add(splitLines[index]);
}
}
}
As you can see above, I already did steps 1 - 4, unfortunately, at the moment each word goes into different row.
Here is bigger part of my xaml file:
<ItemsControl x:Name="itemsControl"
ItemsSource="{Binding itemsControl}"
FontSize="24">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="Auto"
Margin="0 12"
HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Column="0"
Grid.Row="0"
Orientation="Horizontal">
<TextBlock Text="{Binding }" />
</StackPanel>
<StackPanel Grid.Column="1"
Grid.Row="0"
Orientation="Horizontal">
<TextBlock Text="{Binding }" />
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I put two TextBlocks instead of 5 for now. How to make each of them getting different binding path to link with different array element?
----------UPDATE----------
I edited TextBlocks so each of them points to different array element like so:
<StackPanel Grid.Column="0"
Grid.Row="0"
Orientation="Horizontal">
<TextBlock Text="{Binding Path=splitLines[0]}" />
</StackPanel>
<StackPanel Grid.Column="1"
Grid.Row="0"
Orientation="Horizontal">
<TextBlock Text="{Binding Path=splitLines[1]}" />
</StackPanel>
I don't get any errors at this point, but unfortunately no data is displayed at all. Why binding to array element is not working?
You can do this quite easily setting ItemsPanel of ItemsControl
EDIT
Added ItemTemplate to ItemsControl
Say you had a MainPage.xaml
<ItemsControl x:Name="WordsList"
ItemsSource="{Binding listOfWords}">
<!-- Ensures each item is displayed horizontally -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--if you want each line in seperate TextBlock with more control-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding}" FontSize="22" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In code behind MainPage.xaml.cs
public sealed partial class MainPage : Page
{
//Your intial string
string words = "I am Line 1; I am Line 2; I am Line 3";
//Using an ObservableCollection as works well with data binding
public ObservableCollection<string> listOfWords { get; set; } =
new ObservableCollection<string>();
public MainPage()
{
this.InitializeComponent();
//set the DataContext to this page
this.DataContext = this;
Loaded += (s, args) =>
{
var splitWords = words.Split(';');
foreach(var word in splitWords)
{
//each line is added to listOfWords and can then be accessed by the ItemsControl as the ItemsSource
//is set to listOfWords
listOfWords.Add(word);
}
};
}
}
EDIT2
to bind to the array you will need a public property eg
public string[] splitLines { get; set; }

Unable to change background colour for new button

I am using XAML and MVVM pattern for an application.
In part of my application I have a window split into 2 parts, a left column and right hand column/area
In the left column are a row of buttons ( generated on loading the window )
The right column/area will display a group of buttons depending on which button the user clicks in the left column, so for example say I have 4 department buttons in the left column, each department button will have a different number of stock items.
If I click button 1 in the left column, I do some code in the viewmodel to fetch the names of items in that department. And then I build a new observableCollection to display in the right column/area.
So this isnt a problem, the right amount of buttons get generated each time ok.
However when I try to change the background colour dynamically in the ViewModel, the colour is NOT upodated in the view.
The strange thing is, I can change the content, forecolour and other properties but just not the background colour. It appears the background will only generate correctly if loaded and run time. I cant change it otherwise while using the window.
I have tried Brushes, creating and assigning new style and even clearing the dependancy property of the button ( .ClearValue(Button.BackgroundProperty) )
Would any one know how i can get the background to change colour while the window is open and when i want to generate a set of buttons dynamically in my viewmodel?
Many thanks all... I have attached my XAML and C# snippet, the
XAML :
<dxd:DockLayoutManager Name="dlSalesScreen">
<dxd:DockLayoutManager.LayoutRoot>
<dxd:LayoutGroup Name="Root" Orientation="Horizontal" AllowSplitters="False">
<dxd:LayoutPanel AllowClose="False" AllowRename="False"
Caption="Departments" HorizontalScrollBarVisibility="Hidden"
CaptionAlignMode="AutoSize"
CaptionImageLocation="BeforeText" ShowPinButton="False" >
<!-- Scrollviewer for department buttons-->
<ScrollViewer x:Name="deptScrollviewer" Grid.Row="0" Grid.Column="0" Width="185" Height="Auto" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" PanningMode="VerticalOnly">
<ItemsControl ItemsSource="{Binding Departments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VerticalAlignment="Top" Height="Auto" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</dxd:LayoutPanel>
<dxd:LayoutPanel AllowClose="False" AllowRename="False"
Caption="Available stock in department" Width="Auto"
CaptionAlignMode="AutoSize"
CaptionImageLocation="BeforeText" ShowPinButton="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<!-- Scrollviewer for stock buttons-->
<ScrollViewer x:Name="stockScrollViewer" Grid.Row="0" Width="Auto" Height="Auto" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="VerticalOnly">
<ItemsControl ItemsSource="{Binding StockItems, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel VerticalAlignment="Top" HorizontalAlignment="Left" Orientation="Horizontal" Width="400" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<Rectangle Grid.Row="1" Margin="0,0,0,0" Height="Auto" Fill="{StaticResource BottomRectangleGradient}" />
<Grid Name="gridButtonHolder" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<GroupBox x:Name="grpStockItem" Grid.Column="0" Header="Selected Item" HorizontalAlignment="Left" Width="200" >
<Label x:Name="lblStockName" Content="{Binding SelectedStockItemLabel}" HorizontalAlignment="Left" />
</GroupBox>
<Button Name="btnSave" Content="Apply" Command="{Binding ConfirmSelectionCommand}" dxc:ThemeManager.ThemeName="Office2007Blue" Grid.Column="1" Width="110" Height="42" Margin="0,8,0,0" />
<Button Name="btnClose" Content="Cancel" dxc:ThemeManager.ThemeName="Office2007Blue" Grid.Column="2" Width="110" Height="42" Margin="0,8,0,0" />
</Grid>
</Grid>
</dxd:LayoutPanel>
</dxd:LayoutGroup>
</dxd:DockLayoutManager.LayoutRoot>
</dxd:DockLayoutManager>
C#
void DeptClicked(object sender, RoutedEventArgs e)
{
SelectedDeptID = Convert.ToInt32(((Button)sender).Tag.ToString());
_stockButtons = new ObservableCollection<Button>();
if (StockItemCount > 0)
{
for (int i = 0; i < StockItemCount; i++)
{
//_stockButtons.Add(new Button());
Button btn = new Button();
btn.Background = Brushes.Aquamarine;
btn.Height = 100;
btn.Width = 100;
btn.Content = i.ToString();
_stockButtons.Add(btn);
}
}
RaisePropertyChanged("StockItems");
}
public ObservableCollection<Button> Departments
{
get
{
if (_deptButtons == null)
{
_deptButtons = new ObservableCollection<Button>();
for (int i = 0; i < DeptCount; i++)
{
Button button = new Button();
button.Content = DepartmentNames[i];
button.Tag = DepartmentIDs[i].ToString();
button.Click += new RoutedEventHandler(DeptClicked);
button.Width = 128;
button.Height = 100;
_deptButtons.Add(button);
}
}
return _deptButtons;
}
}
Try something like that:
Button btn = new Button();
btn.Background = Brushes.Green;
btn.Height = 100; btn.Width = 100;
btn.Content = i.ToString();
ThemeManager.SetThemeName(btn, "None");
_stockButtons.Add(btn);
Class ThemeMagager is in namespace DevExpress.Xpf.Core.

Categories