TextBlock in UserControl not displaying text - c#

I'm trying to create a Nonogram (aka PuzzleCross) puzzle grid in C#/WPF, and have created two UserControls to contain the row and column keys. Each UserControl consists of a Border containing a TextBlock, with a DependencyProperty named TextControl to make the Text property accessible outside of the UserControl. Everything works fine except that the text isn't actually displayed when run. The TextControl contains the correct text, as tested with a MouseDown event and a MessageBox, but for some reason the text just isn't there.
Can anyone help me figure out what I'm missing? I have a feeling it's a simple thing, but I'm just not seeing it.
Horizontal UserControl:
<Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Center" Height="10" Width="100">
<TextBlock Text="{Binding ElementName=HorizontalRowLabel, Path=TextContent}" Foreground="Black" FontSize="6" MouseDown="TextBlock_MouseDown"/>
</Border>
Horizontal C#:
public partial class HorizontalRowLabel : UserControl
{
public static readonly DependencyProperty TextContentProperty = DependencyProperty.Register("TextContent", typeof(string),
typeof(HorizontalRowLabel), new FrameworkPropertyMetadata(""));
public string TextContent
{
get { return (string)GetValue(TextContentProperty); }
set { SetValue(TextContentProperty, value); }
}
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show(TextContent);
}
public HorizontalRowLabel()
{
InitializeComponent();
}
}
//Adds text HorizontalRowLabel UserControl, then adds HRL to Grid.
public void InitRowKeys(Grid puzzle)
{
for(int i = 0; i < HorizontalKeys.Length; i++)
{
RowDefinition row = new RowDefinition();
HorizontalRowLabel hrow = new HorizontalRowLabel();
row.Height = new GridLength(10);
for(int j = 0; j < HorizontalKeys[i].Length; j++)
{
if(HorizontalKeys[i].Length == 0 || j == HorizontalKeys[i].Length - 1)
{
hrow.TextContent += HorizontalKeys[i][j].ToString();
hrow.Foreground = Brushes.Black;
hrow.SetValue(Grid.RowProperty, i);
hrow.SetValue(Grid.ColumnProperty, 0);
hrow.FontSize = 6;
hrow.HorizontalAlignment = HorizontalAlignment.Right;
hrow.VerticalAlignment = VerticalAlignment.Center;
}
else
{
hrow.TextContent += HorizontalKeys[i][j].ToString() + " ";
hrow.SetValue(Grid.RowProperty, i);
hrow.SetValue(Grid.ColumnProperty, 0);
hrow.FontSize = 6;
hrow.HorizontalAlignment = HorizontalAlignment.Right;
hrow.VerticalAlignment = VerticalAlignment.Center;
}
}
//puzzle.Margin = new Thickness(0,50,0,0);
hrow.Width = 100;
hrow.Height = 30;
puzzle.RowDefinitions.Add(row);
puzzle.Children.Add(hrow);
}
}

A Binding like
Text="{Binding ElementName=HorizontalRowLabel, Path=TextContent}"
only works if you have assigned the x:Name attribute to the UserControl:
<UserControl ... x:Name="HorizontalRowLabel">
...
</UserControl>
That is however not necessary with a RelativeSource Binding:
Text="{Binding TextContent, RelativeSource={RelativeSource AncestorType=UserControl}}"

Related

How to create and use matrix of (color) boxes C# WPF

I have to do some sort of game with WPF App that contain some matrix of color boxes (ex. 10x10). On-click at some it must eliminate itself and surrounding boxes with the same color if there are more than 3, and after elimination these boxes grant some random color.
I'm fairly new in WPF apps, but I have some knowledge of C# Programming and I can't figure out from where I should start. Most difficult part for me is "spawning" this boxes and use it like a matrix.
So far I found some project that I thought it will help me, but not really.
Can someone navigate from where I can start and which is most relevant way to do this.
Thank you.
ItemsControl + UniformGrid as a Panel is a good choice to display a matrix
view
<ItemsControl Name="Board">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate >
<UniformGrid Rows="10" Columns="10"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Transparent"
BorderBrush="Black"
BorderThickness="1"
MouseDown="CellClick"
Margin="2"
Tag="{Binding}">
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
code-behind
public partial class MainWindow : Window
{
List<Point> _board;
public MainWindow()
{
InitializeComponent();
int rows = 10;
int columns = 10;
_board = new List<Point>();
for(int r = 0; r<rows; r++)
for (int c = 0; c < columns; c++)
_board.Add(new Point(r, c));
Board.ItemsSource = _board;
}
private void CellClick(object sender, MouseButtonEventArgs e)
{
var border = (Border)sender;
var point = (Point) border.Tag;
}
}
you can create and use more complex type instead of Point and improve ItemTemplate to continue development. current ItemTemplate is nothing more that a rectangle
I used code-behind for demonstration, but in wpf MVVM in a preferred approach
EDIT extended example
in most cases you don't have to work with UI elements directly
to support different Colors I will create a custom class
public class MatrixElement
{
private string _color;
public MatrixElement(int x, int y)
{
X = x;
Y = y;
}
public int X { get; private set; }
public int Y { get; private set; }
public string Color
{
get { return _color; }
set
{
_color = value;
if (ColorChanged != null)
ColorChanged(this, EventArgs.Empty);
}
}
public event EventHandler ColorChanged;
}
window code has changed accordingly
List<MatrixElement> _board;
public MainWindow()
{
InitializeComponent();
int rows = 10;
int columns = 10;
_board = new List<MatrixElement>();
for (int r = 0; r < rows; r++)
for (int c = 0; c < columns; c++)
_board.Add(new MatrixElement(r, c){Color = "Green"});
Board.ItemsSource = _board;
}
private void CellClick(object sender, MouseButtonEventArgs e)
{
var border = (Border)sender;
// each point has unique {X;Y} coordinates
var point = (MatrixElement)border.Tag;
// changing color in item view model
// view is notified by binding
point.Color = "#00BFFF";
}
ItemTemplate was modified a bit
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="{Binding Path=Color}"
BorderBrush="Black"
BorderThickness="1"
MouseDown="CellClick"
Margin="2"
Tag="{Binding}">
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>

How To Dynamically Add Arbitrary Number Of Line Series To WPF Toolkit Chart?

Is it possible to draw multiple lines in a single toolkit chart where line numbers are determined at run-time? I prefer a MVVM way of binding lines to a collection. For example below, there is only one "LineSeries" to be shown, but what if I want to show multiple lines. Thank you!
<ch:Chart.Series>
<ch:LineSeries Title="{Binding Title}"
ItemsSource="{Binding DataPoints}"
IndependentValueBinding="{Binding Path=X}"
DependentValueBinding="{Binding Path=Y}">
</ch:LineSeries>
</ch:Chart.Series>
EDIT 3 - Adding a Test button:
XAML:
<Grid>
<chartingToolkit:Chart x:Name="chart1" HorizontalAlignment="Left" Margin="10,10,0,0" Title="Chart Title" VerticalAlignment="Top" Width="498" Height="277">
</chartingToolkit:Chart>
<Button x:Name="button1" Content="ADD" HorizontalAlignment="Center" Margin="226,292,217.4,0" VerticalAlignment="Top" Width="75" Click="button1_Click"/>
</Grid>
Window:
int index;
MyViewModel2 viewModel;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
viewModel = new MyViewModel2();
DataContext = viewModel;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
PointCollection pc = new PointCollection();
for (int i = 1; i <= 10; i++)
pc.Add(new Point { X = i, Y = i * (2 + index) });
LineSeries series1 = new LineSeries();
series1.DependentValuePath = "Y";
series1.IndependentValuePath = "X";
series1.ItemsSource = pc;
chart1.Series.Add(series1);
viewModel.MyList.Add(pc);
index++;
}
EDIT 2 - Adding Series dynamically:
XAML:
<chartingToolkit:Chart x:Name="chart1" Margin="0" Title="Chart Title">
</chartingToolkit:Chart>
ViewModel:
public class MyViewModel2
{
public List<PointCollection> MyList { get; set; }
public MyViewModel2()
{
MyList = new List<PointCollection>();
}
}
Window:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
viewModel = new MyViewModel2();
DataContext = viewModel;
PointCollection pc1 = new PointCollection();
PointCollection pc2 = new PointCollection();
for (int i = 1; i <= 10; i++)
{
pc1.Add(new Point { X = i, Y = i * 2 });
pc2.Add(new Point { X = i, Y = i * 3 });
}
LineSeries series1 = new LineSeries();
series1.DependentValuePath = "Y";
series1.IndependentValuePath = "X";
series1.ItemsSource = pc1;
chart1.Series.Add(series1);
viewModel.MyList.Add(pc1);
LineSeries series2 = new LineSeries();
series2.DependentValuePath = "Y";
series2.IndependentValuePath = "X";
series2.ItemsSource = pc2;
chart1.Series.Add(series2);
viewModel.MyList.Add(pc2);
}
EDIT 1 - This should get you going:
XAML:
<Grid>
<chartingToolkit:Chart Margin="0" Title="Chart Title">
<chartingToolkit:LineSeries DependentValuePath="Y" IndependentValuePath="X" ItemsSource="{Binding MyPointCollection1}"/>
<chartingToolkit:LineSeries DependentValuePath="Y" IndependentValuePath="X" ItemsSource="{Binding MyPointCollection2}"/>
</chartingToolkit:Chart>
</Grid>
ViewModel:
public class MyViewModel
{
public PointCollection MyPointCollection1 { get; set; }
public PointCollection MyPointCollection2 { get; set; }
public MyViewModel()
{
MyPointCollection1 = new PointCollection();
MyPointCollection2 = new PointCollection();
}
}
Window:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
viewModel = new MyViewModel();
DataContext = viewModel;
for (int i = 1; i <= 10; i++)
{
viewModel.MyPointCollection1.Add(new Point { X = i, Y = i * 2 });
viewModel.MyPointCollection2.Add(new Point { X = i, Y = i * 3 });
}
}

Custom UserControl not showing up in ListBox

I have a problem displaying custom UserControls in my ListBox programmatically. I just can't seem to figure out what is wrong. The list-item shows up without image or text.
My project consists of:
MainWindow.xaml
MainWindow.xaml.cs
cvMenuItem.xaml
cvMenuItem.xaml.cs
Code of MainWindow.xaml.cs
private void cvMenuItem_MouseLeftButtonUp_1(object sender, MouseButtonEventArgs e)
{
lstContacts.Items.Clear();
cvMenuItem test = new cvMenuItem("test",
Environment.GetEnvironmentVariable("USERPROFILE") + #"\Downloads\images.jpg");
lstContacts.Items.Add(test);
}
Code of cvMenuItem.xaml.cs
public partial class cvMenuItem : UserControl
{
public cvMenuItem()
{
InitializeComponent();
}
public cvMenuItem(string text, string Logo)
{
this.Height = 50;
this.Width = 186;
txtService = new TextBlock() { Width = 100, Height = 50 };
imgLogo = new Image() { Width = 50, Height = 50 };
//Just found out, adding the objects as childeren partially works
this.AddChild(imgLogo);
//But I can't add txtService as Childeren
//this.AddChild(txtService);
this.Services = text;
this.Logo = Logo;
}
public string Services
{
get{ return txtService.Text.ToString() }
set
{
txtService.Text = value;
}
}
public string Logo
{
get{ return imgLogo.Source.ToString(); }
set
{
var uriSource = new Uri(value);
imgLogo.Source = new BitmapImage(uriSource);
}
}
My cvMenuItem.xaml.cs
<UserControl x:Class="WpfApplication1.cvMenuItem"
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" Height="50" Width="186">
<Grid Width="186" VerticalAlignment="Top">
<Image Name="imgLogo" Height="50" Width="50" HorizontalAlignment="Left" VerticalAlignment="Top" OpacityMask="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}" />
<TextBlock Name="txtService" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Bottom" Height="18" Width="121" Margin="70,0,0,18" RenderTransformOrigin="0.499,1.932"/>
</Grid>
</UserControl>
First of all you need to call InitializeComponent in the custom constructor you have added, as that is needed to process the XAML properly. Otherwise all the controls you add in the XAML will be null when running the application.
Additionally it makes no sense to create the TextBlock and Image again in the code-behind. You just have to use the ones created in the XAML.
So to get it working, change the code in the constructor to the following:
public cvMenuItem(string text, string Logo)
{
InitializeComponent();
this.Height = 50;
this.Width = 186;
this.Services = text;
this.Logo = Logo;
}

How do you get Canvas.SetTop to work on ItemsCollection items?

Below is the complete program (XAML and code behind). When started you will see:
When you click [Show/Hide] you see:
When you uncheck Home and Documents and click [Apply] you see:
As you can see, the Home and Documents folder disappear, but the other folders do not move up. Looking at the two list boxes you see three items per line, folder label, the value for Canvas.SetTop, and True: Visibility.Visible or False: Visibility.Collapsed. The Original Placement list box are the original values. Each time you click [Apply] the New Placement List box shows that new values. The Visibility value gets applied and the result is visible, either the folder is there or it is not. However, applying the Canvas.SetTop does not work. And you can see that the values have changed for the Canvas.SetTop. And, if you look at the code, you see that it is applied to the same UIElement as the Visibility is applied to:
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work and
iec.Visibility = f.FolderVisible; // this does work?
I need the folders that are visible to move up to take the place of those that are collapsed. Canvas.SetTop is not working. How can I get the Folders to move?
Here is the Solution Explorer pane so that you know what is needed:
Here is the XAML:
<Window x:Class="FolderTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:FolderTest"
Title="Folder Test"
Width="450"
Height="410">
<Grid Margin="10">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<!-- Folder Show/Hide Button -->
<Button Name="FolderOptions" Content="Show/Hide" FontSize="8" FontWeight="Bold" Width="50" Height="20" Margin="116, 0, 0, 0" Click="Event_ShowHide_ButtonClick" />
<!-- Folders -->
<Grid>
<Canvas Name="xFolders"
Width="170"
Height="309"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="0,10,0,0">
<Canvas.Resources>
<local:Folders x:Key="myFolders" />
</Canvas.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource myFolders}}">
<ItemsControl.Template>
<ControlTemplate>
<ItemsPresenter />
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
</DataTemplate.Resources>
<Canvas Name="FolderCanvas"
Tag="{Binding Path=Number}"
Visibility="{Binding Path=FolderVisible}">
<Path Data="{Binding Path=FolderPath}"
Stroke="{Binding Path=Brush}"
Fill="{Binding Path=Brush}"/>
<StackPanel Orientation="Horizontal"
Canvas.Left="5"
Canvas.Top="2">
<TextBlock Text="{Binding Path=Label}"
FontSize="12"
Margin="20, 0, 0, 0" />
</StackPanel>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top"
Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="10, 0, 0, 0">
<TextBlock Text="Original Placemet:" />
<ListBox Name="OriginalPlacement" />
<Button Name="Refresh" Content="New Placemet:" Margin="0, 10, 0, 0" Click="Event_OriginalPlacement_ButtonClick" />
<ListBox Name="NewPlacement" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
Here is the C# Code Behind:
using System;
using System.Collections.Generic;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows;
using System.Collections.ObjectModel;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace FolderTest
{
public struct GD
{
public static MainWindow MainWindow = null;
public static Button Refresh = null;
}
public partial class MainWindow : Window
{
public MainWindow()
{
GD.MainWindow = this;
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
GD.Refresh = Refresh;
Folders.Placement(ref OriginalPlacement);
}
private void Event_ShowHide_ButtonClick(object sender, RoutedEventArgs e)
{
var pu = Folders.FolderShowPopUp();
pu.PlacementTarget = FolderOptions;
pu.IsOpen = true;
}
private void Event_OriginalPlacement_ButtonClick(object sender, RoutedEventArgs e)
{
Folders.Placement(ref NewPlacement);
}
}
public class Folder
{// Specification for each folder tab seen on the left.
public int Number { get; set; } // Folder sequence number top to bottom starging with 1.
public string Label { get; set; } // The label that appears on the folder tab itself.
public double Top { get; set; } // Position of the upper left corner of this folder.
public SolidColorBrush Brush { get; set; } // Solid Color Brush.
public string FolderPath { get; set; } // Contains the geometric path to draw this folder and its tab.
public Visibility FolderVisible { get; set; } // This folder's Show/Hide flag.
public Folder(int Number, string Label, double Top, SolidColorBrush Brush, string FolderPath, Visibility FolderVisible)
{
this.Number = Number;
this.Label = Label;
this.Top = Top;
this.Brush = Brush;
this.FolderPath = FolderPath;
this.FolderVisible = FolderVisible;
}
}
public class Folders : ObservableCollection<Folder>
{
public static ObservableCollection<Folder> Me = null;
private static string[] Labels = new string[]
{
"Personal",
"Health",
"Finances",
"Home",
"Employment",
"Insurance",
"Documents",
"Contacts",
"Journal"
};
private static Dictionary<string, Tuple<bool, bool>> LabelData = new Dictionary<string, Tuple<bool, bool>>()
{// Label Show Hidable
{Labels[0], new Tuple<bool, bool>(true, false)},
{Labels[1], new Tuple<bool, bool>(true, true)},
{Labels[2], new Tuple<bool, bool>(true, true)},
{Labels[3], new Tuple<bool, bool>(true, true)},
{Labels[4], new Tuple<bool, bool>(true, true)},
{Labels[5], new Tuple<bool, bool>(true, true)},
{Labels[6], new Tuple<bool, bool>(true, true)},
{Labels[7], new Tuple<bool, bool>(true, false)},
{Labels[8], new Tuple<bool, bool>(true, true)}
};
private static string[] FolderColors = new string[]
{
"FF36579E",
"FFDF2024",
"FF16A146",
"FF00B2D4",
"FFF47B20",
"FF9F1F63",
"FF13A89E",
"FFB7B7E7",
"FF50CAF5",
"FFAA9E74",
"FF86787D",
"FF36D146",
};
private static byte[] ARGBColor = new byte[4]; // Byte array for the folder top color.
private static void ColorString2ARGB(ref byte[] bytes, string Hex)
{// Converts 8 char hex string to 4 byte array.
for (int i = 0; i < 8; i += 2)
bytes[i / 2] = Convert.ToByte(Hex.Substring(i, 2), 16);
}
private static int colorIndex = -1; // Initial value of the colorIndex.
private static string NextColor()
{// Returns a 8 char string containing the next top FolderColor. If at end, cycle to beginning.
colorIndex++;
if (colorIndex >= FolderColors.Length) colorIndex = 0;
return FolderColors[colorIndex];
}
private class FolderShow
{
public string Label { get; set; }
public bool Show { get; set; }
public bool Hidable { get; set; }
public FolderShow(string label, bool show, bool hidable)
{
Label = label;
Show = show;
Hidable = hidable;
}
}
private static List<FolderShow> FolderShowList = null;
public static Popup FolderShowPopUp()
{
var pu = new Popup()
{
Placement = PlacementMode.Right,
AllowsTransparency = true,
StaysOpen = false
};
var b = new Border()
{
BorderThickness = new Thickness(2),
BorderBrush = new SolidColorBrush() { Color = Colors.Black },
CornerRadius = new CornerRadius(5),
Background = new SolidColorBrush() { Color = Colors.White }
};
var sp = new StackPanel()
{
Orientation = Orientation.Vertical,
Margin = new Thickness(10)
};
var tb = new TextBlock()
{
Text = "Checked Folders will be Displayed: ",
FontSize = 16,
FontWeight = FontWeights.Bold,
};
sp.Children.Add(tb);
foreach (var fs in FolderShowList)
{
var cb = new CheckBox()
{
Content = fs.Label,
IsChecked = fs.Show,
IsEnabled = fs.Hidable,
FontSize = 14,
FontWeight = FontWeights.Bold,
Margin = new Thickness(0, 5, 0, 0)
};
cb.Click += new RoutedEventHandler(Event_CheckBoxFolderList_Click);
sp.Children.Add(cb);
}
var bp = new StackPanel()
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(0, 5, 0, 0)
};
var ba = new Button() { Content = "Apply", Width = 50, Height = 25, BorderBrush = new SolidColorBrush(Colors.Transparent), Tag = pu };
ba.Click += new RoutedEventHandler(Event_ApplyFolderList_Click);
bp.Children.Add(ba);
var bc = new Button() { Content = "Cancel", Width = 50, Height = 25, BorderBrush = new SolidColorBrush(Colors.Transparent), Tag = pu, Margin = new Thickness(7, 0, 0, 0) };
bc.Click += new RoutedEventHandler(Event_CancelFolderList_Click);
bp.Children.Add(bc);
sp.Children.Add(bp);
var tbm = new TextBlock()
{
Text = "Disabled folders cannot be hidden.",
Margin = new Thickness(0, 5, 0, 0),
FontSize = 12,
Foreground = new SolidColorBrush() { Color = Colors.Red }
};
sp.Children.Add(tbm);
b.Child = sp;
pu.Child = b;
return pu;
}
private static void Event_CheckBoxFolderList_Click(object sender, RoutedEventArgs e)
{
var cb = (CheckBox)e.Source;
foreach (var fs in FolderShowList)
{
if (fs.Label == cb.Content as string)
{
fs.Show = (bool)cb.IsChecked;
break;
}
}
}
private static void Event_CancelFolderList_Click(object sender, RoutedEventArgs e)
{
((Popup)((Button)e.Source).Tag).IsOpen = false;
}
private static void Event_ApplyFolderList_Click(object sender, RoutedEventArgs e)
{
foreach (var fs in FolderShowList)
{
var sh = LabelData[fs.Label];
LabelData[fs.Label] = new Tuple<bool, bool>(fs.Show, sh.Item2);
}
((Popup)((Button)e.Source).Tag).IsOpen = false;
int p = 0;
foreach (var f in Me)
{
var fs = LabelData[f.Label].Item1 == true;
f.Top = p * folderPositionFactor;
f.FolderVisible = fs ? Visibility.Visible : Visibility.Collapsed;
if (fs) p += 1;
}
foreach (ItemsControl ic in GD.MainWindow.xFolders.Children)
{
for (int i = 0; i < ic.Items.Count; i++)
{
var ie = (UIElement)ic.ItemContainerGenerator.ContainerFromIndex(i);
var iec = Controls.FindChildByType<Canvas>(ie, "FolderCanvas");
if (iec != null)
{
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work and
iec.Visibility = f.FolderVisible; // this does work?
}
}
break;
}
GD.Refresh.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}
public static void Placement(ref ListBox lb)
{
lb.Items.Clear();
foreach (ItemsControl ic in GD.MainWindow.xFolders.Children)
{
for (int i = 0; i < ic.Items.Count; i++)
{
var ie = (UIElement)ic.ItemContainerGenerator.ContainerFromIndex(i);
var iec = Controls.FindChildByType<Canvas>(ie, "FolderCanvas");
if (iec != null)
{
var f = Me[i];
lb.Items.Add(f.Label + ": " + f.Top.ToString() + ", " + (f.FolderVisible == Visibility.Visible ? "True" : "False"));
}
}
break;
}
}
public Folders()
{// The constructor, initializes itself by creating a folder for each label in Labels
FolderShowList = new List<FolderShow>();
for (int i = 0; i < Labels.Length; i++)
{
string n = Labels[i];
bool h = LabelData[Labels[i]].Item2;
bool s = h == true ? LabelData[Labels[1]].Item1 == true : true;
FolderShowList.Add(new FolderShow(n, s, h));
}
SetFolders();
Me = this;
}
const double folderPositionFactor = 31; // 21.80;
public void SetFolders()
{
SolidColorBrush[] scb = new SolidColorBrush[Labels.Length]; // Hold the linear solid color brush to assign to the folder.
Color[] pointerColor = new Color[Labels.Length]; // Hold the color to assign to the folder's solid color brush.
for (int i = 0; i < Labels.Length; i++)
{// Create a solid color brush for each folder.
ColorString2ARGB(ref ARGBColor, NextColor()); // Color into byte array.
Color TmpColor = Color.FromArgb(ARGBColor[0], ARGBColor[1], ARGBColor[2], ARGBColor[3]); // Create top color.
pointerColor[i] = TmpColor;
SolidColorBrush TmpSCB = new SolidColorBrush() { Color = TmpColor };
scb[i] = TmpSCB; // Assign the solid color brush.
}
// All is ready to create the individual folders.
const string folderHeight = "56"; // "44";
Brush FontColor = Brushes.Black; // Initial font color for labels.
string fp = "M0,7 A7,7 90 0 1 7,0 L100,0 105,18 150,18 150,FH 0,FH Z".Replace("FH", folderHeight); // Initial geometric path for folder design.
int afp = 0; // Actual Folder Position.
for (int i = 0; i < Labels.Length; i++)
{// Create the individual folders.
bool fs = FolderShowList[i].Show;
Add(new Folder(
i + 1, // Folder sequence count.
Labels[i], // Folder label.
afp * folderPositionFactor, // Position of top of folder.
scb[i % scb.Length], // Solid color brush.
fp, // Geometric path for folder design.
fs ? Visibility.Visible : Visibility.Collapsed // User Hidden.
)
);
if (fs) afp += 1;
if (i == 0)
{// First folder created, now set values for remaining folders.
FontColor = Brushes.White;
fp = "M0,25 A7,7 90 0 1 7,18 L13,18 18,0 100,0 105,18 150,18 150,FH 0,FH Z".Replace("FH", folderHeight);
}
}
}
}
public static class Controls
{
public static T FindChildByType<T>(DependencyObject parent, string childName) where T : DependencyObject
{
if (parent == null) return null; // No parent, get out of here.
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChildByType<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
}
}
Thanks for any help that you can provide.
I finally figured out the answer, and it is very simple.
This is the code to be changed (the center line only):
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work
iec.Visibility = f.FolderVisible; // and this does work?
This is the new code (only the center line is changed, actually, only one character is removed):
var f = Me[i];
Canvas.SetTop(ie, f.Top);
iec.Visibility = f.FolderVisible;
That is, change Canvas.SetTop(iec, f.Top); to Canvas.SetTop(ie, f.Top); that is, only change iec to ie and all works fine.

Shuffling List<Button> loses connection to mainpage.xaml

My app creates a list of 20 Buttons that are placed on mainpage.xaml:
private List<Button> CreateList()
{
for (int i = 0; i <= 19; i++)
{
string name = string.Format("button{0}", i+1);
Button buttt = new Button();
buttt.Name = name;
buttt.Content = i + 1;
buttt.Height = 72;
buttt.HorizontalAlignment = HorizontalAlignment.Left;
buttt.VerticalAlignment = VerticalAlignment.Top;
buttt.Width = 88;
buttt.Click += new RoutedEventHandler(this.button_Click);
GameGrid.Children.Add(buttt);
myList.Insert(i, buttt);
}
Now if I try to shuffle this list, it seems to lose its connection to the actual buttons on the page.
private void Shuffle(List<Button> list)
{
//list[1].Content = "DING!";
Random rand = new Random();
int n = list.Count;
while (n > 1)
{
n--;
int k = rand.Next(n + 1);
Button value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Note that if i un-comment //list[1].Content = "DING!"; and comment out the rest of this method the button's content IS changed on the screen. So I'd assume the link is broken during the shuffle.
So my problem is that when I run this code, the buttons are displayed but are still in order from 1 to 20, instead of being shuffled randomly like I've intended.
Thanks for the help!
Edit: Here is the full code with Chris's suggestions:
private List<Button> CreateList(List<Marginz> myMargin)
{
for (int i = 0; i <= 19; i++)
{
string name = string.Format("button{0}", i+1);
Button buttt = new Button();
buttt.Name = name;
buttt.Content = i + 1;
buttt.Height = 72;
buttt.HorizontalAlignment = HorizontalAlignment.Left;
buttt.VerticalAlignment = VerticalAlignment.Top;
buttt.Width = 88;
buttt.Click += new RoutedEventHandler(this.button_Click);
Thickness myThickness = new Thickness();
myThickness.Left = myMargin[i].left;
myThickness.Top = myMargin[i].top;
myThickness.Right = myMargin[i].right;
myThickness.Bottom = myMargin[1].bottom;
buttt.Margin = myThickness;
//GameGrid.Children.Add(buttt);
myList.Insert(i, buttt);
}
return myList;
}
And here is where it's called:
private void EasyButton_Click(object sender, RoutedEventArgs e)
{
DifficultyCanvas.Visibility = System.Windows.Visibility.Collapsed;
ReadyCanvas.Visibility = System.Windows.Visibility.Visible;
//set difficulty attributes
difficulty = "Easy";
var myMarg = CreateMarginList(marg);
var buttons = CreateList(myMarg);
Shuffle(buttons);
foreach (var button in buttons)
{
GameGrid.Children.Add(button);
}
}
Edit for more explanation:
About the Margins. I've created a class called Marginz:
public class Marginz
{
public Marginz()
{
//Constructor
}
public int left { get; set; }
public int top { get; set; }
public int right { get; set; }
public int bottom { get; set; }
}
"marg" is a List of this type:
List<Marginz> marg = new List<Marginz>(20);
And CreateMarginList() does this:
public List<Marginz> CreateMarginList(List<Marginz> myMarg)
{
Marginz one = new Marginz();
one.left = 28;
one.top = 186;
one.right = 0;
one.bottom = 0;
myMarg.Insert(0, one);
Marginz two = new Marginz();
two.left = 133;
two.top = 186;
two.right = 0;
two.bottom = 0;
myMarg.Insert(1, two);
etc all the way to twenty. Then return myMarg;
So every Button has a unique margin placing it in the Grid.
Shuffling the myList collection won't change the order they appear on the page. That is determined by the order you add them to the GameGrid in your CreateList method. What you can do instead is create them all, shuffle the list, then add them to the Children listing.
So remove the GameGrid.Children.Add call in CreateList (note, I kinda tweaked the code there, I'm assuming you weren't posting full code)
private List<Button> CreateList()
{
var myList = new List<Button>();
for (int i = 0; i <= 19; i++)
{
string name = string.Format("button{0}", i+1);
Button buttt = new Button();
buttt.Name = name;
buttt.Content = i + 1;
buttt.Height = 72;
buttt.HorizontalAlignment = HorizontalAlignment.Left;
buttt.VerticalAlignment = VerticalAlignment.Top;
buttt.Width = 88;
buttt.Click += new RoutedEventHandler(this.button_Click);
myList.Add(buttt);
}
return myList;
}
Perform your shuffle, then add them:
var buttons = CreateList();
Shuffle(buttons);
foreach(var button in buttons)
{
GameGrid.Children.Add(button);
}
EDIT: From your full code that you posted, the problem is that because all of the buttons are in a Grid control, their positioning is dictated by which row/column their in and their Margin (which controls their positioning within that cell). If you do not explicitly define their row/column, which you do not, then they're assumed to be in the first row/column. In this case, their margins, which are not shuffled, dictate their positioning.
I think in this case, either build the Grid with cells, or probably most easily: simply shuffle the myMargin list instead before creating the list! The buttons will be added in order, but they'll be given random positions.
var myMargin = CreateMargins(); //wherever that's done
Shuffle(myMargin); //you'll have to change the signature to work against List<Thickness> instead
var buttons = CreateList(myMargin); //add back the GameGrid.Children.Add call
//notice, no longer a call to shuffle the buttons
Might not be the best solution, but I think this will give you the same effect you were going for.
Your logic is flawed. You're putting the buttons in a grid, and positioning them with the margin. Since you're using the margin to position them, their position won't change no matter in which order you add them to the grid.
A few ways to do what you're trying to do:
Apply the margin after the button list has been shuffled
Use a Stackpanel instead of a grid (and remove the margin)
Use the grid as it's intended to be: create some rows, and assign each button to a row
<Grid>
<Grid.RowDefinitions>
<Grid.RowDefinition />
<Grid.RowDefinition />
<Grid.RowDefinition />
etc...
</Grid.RowDefinitions>
<Grid>
Then in the code behind, set the row of each button:
Grid.SetRow(myButton, 1);
You could just shuffle the index of the buttons in the UIElementCollection, But you may have to switch grid types as setting all the Margins explicity in your code will not allow it to shuffle.
Are you able to Use StackPanel, WrapPanel or UniformGrid to layout your controls ??
Example based on UniformGrid (same will work for all grids unless you have set Margins on the controls).
I know its not an answer to you current question but it may point you in the right direction.
private void Shuffle(UIElementCollection list)
{
Random rand = new Random();
int n = list.Count;
while (n > 1)
{
n--;
int k = rand.Next(n + 1);
Button value = list.OfType<Button>().ElementAt(n);
list.Remove(value);
list.Insert(k, value);
}
}
Example:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="199" Width="206" Name="UI">
<Grid>
<UniformGrid Name="GameGrid" Margin="0,48,0,0" Height="112" VerticalAlignment="Top">
<Button Content="Button 1" />
<Button Content="Button 2" />
<Button Content="Button 3" />
<Button Content="Button 4" />
<Button Content="Button 5" />
</UniformGrid>
<Button Content="Shuffle" Height="23" HorizontalAlignment="Left" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Shuffle(GameGrid.Children);
}
private void Shuffle(UIElementCollection list)
{
Random rand = new Random();
int n = list.Count;
while (n > 1)
{
n--;
int k = rand.Next(n + 1);
Button value = list.OfType<Button>().ElementAt(n);
list.Remove(value);
list.Insert(k, value);
}
}
}

Categories

Resources