Conways game of life trying to get the code behind working - c#

i have four classes
public class BoolToCol : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool v = (bool)value;
if (v == true)
{
return new SolidColorBrush(Colors.Red);
}
else
{
return new SolidColorBrush(Colors.Blue);
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Cell : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool _ItsAlive;
public int _CellIndex;
public bool ItsAlive {
get {return _ItsAlive;}
set{
_ItsAlive = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ItsAlive"));
}
}
}
public int CellIndex
{
get { return _CellIndex; }
set
{
_CellIndex = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CellIndex"));
}
}
}
}
<Window x:Class="Pucketts_ConWaysGameOfLife.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Game of Life" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40*"/>
<ColumnDefinition Width="58*"/>
</Grid.ColumnDefinitions>
<Grid Background="Black"
Grid.Column="0">
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Width"
Value="80"/>
<Setter Property="Height"
Value="20"/>
<Setter Property="HorizontalAlignment"
Value="Left"/>
<Setter Property="VerticalAlignment"
Value="Top"/>
<Setter Property="BorderThickness"
Value="0"/>
<Setter Property="Foreground"
Value="Red"/>
<Setter Property="Background"
Value="Black"/>
<Setter Property="Grid.Row"
Value="3"/>
</Style>
<Style TargetType="Slider">
<Setter Property="TickFrequency"
Value="1"/>
<Setter Property="SmallChange"
Value="1"/>
<Setter Property="IsSnapToTickEnabled"
Value="True"/>
<Setter Property="LargeChange"
Value="10"/>
<Setter Property="Minimum"
Value="0"/>
<Setter Property="Width"
Value="80"/>
</Style>
<SolidColorBrush x:Key="foregroundBrush"
Color="Red"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="68"/>
<ColumnDefinition Width="76"/>
<ColumnDefinition Width="68"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Grid.Row="2"
Content="GenSlide"
Foreground="{StaticResource foregroundBrush}"/>
<Label Grid.Column="0"
Grid.Row="1"
Content="Width"
Foreground="{StaticResource foregroundBrush}"/>
<Label Grid.Column="0"
Grid.Row="0"
Content="Height"
Foreground="{StaticResource foregroundBrush}"/>
<Slider Grid.Column="1"
Grid.Row="2"
LargeChange="2"
x:Name="GenSlide"
Maximum="15"/>
<Slider Grid.Column="1"
Grid.Row="1"
x:Name="WidthSlide"
Maximum="100"/>
<Slider Grid.Column="1"
Grid.Row="0"
x:Name="HeightSlide"
Maximum="100"/>
<Button Grid.Column="2"
Click="SetGrid"
Content="Set Grid"/>
<Button Grid.Column="0"
Click="Random"
Content="Random"/>
<Label HorizontalAlignment="Left"
Grid.Column="2"
Grid.Row="2"
x:Name="GenNum"
Content="{Binding ElementName=GenSlide, Path= Value}"
Foreground="{StaticResource foregroundBrush}"/>
<Label HorizontalAlignment="Left"
Grid.Column="2"
Grid.Row="1"
x:Name="WidthNum"
Content="{Binding ElementName=WidthSlide, Path= Value}"
Foreground="{StaticResource foregroundBrush}"/>
<Label HorizontalAlignment="Left"
Grid.Column="2"
Grid.Row="0"
x:Name="HeightNum"
Content="{Binding ElementName=HeightSlide, Path= Value}"
Foreground="{StaticResource foregroundBrush}"/>
<Button Grid.Column="1"
Click="Play"
Content="Play"/>
<Button Grid.Column="1"
VerticalAlignment="Bottom"
Click="Next"
Margin="0,0,0,-10"
Content="Next"/>
</Grid>
<UniformGrid x:Name="Board"
Grid.Column="1">
</UniformGrid>
</Grid>
</Window>
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
Cell[] CellA;
int _NumOfColumns;
int CellIndex;
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
this.InitializeComponent();
}
public int NumOfColumns
{
get { return _NumOfColumns; }
set
{
_NumOfColumns = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("NumOfColumns"));
}
}
}
private void Initializeboard()
{
NumOfColumns = (int) (this.HeightSlide.Value);
CellA = new Cell[NumOfColumns * (int) (this.WidthSlide.Value)];
for (int i = 0; i < this.HeightSlide.Value * this.WidthSlide.Value; i++)
{
Rectangle rec = new Rectangle();
rec.Stroke = Brushes.Green;
rec.MouseLeftButtonDown += new MouseButtonEventHandler(rec_MouseLeftButtonDown);
this.Board.Children.Add(rec);
Cell c = new Cell();
c.CellIndex = i;
Binding newB = new Binding("ItsAlive");
newB.Source = c;
newB.Converter = new BoolToCol();
rec.SetBinding(Rectangle.FillProperty, newB);
CellA[i] = c;
}
}
public void rec_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
foreach (UIElement item in this.Board.Children)
{
if (item.IsMouseOver)
{
Cell c = (item as Rectangle).GetBindingExpression(Rectangle.FillProperty).ResolvedSource as Cell;
c.ItsAlive = !c.ItsAlive;
//c.CellIndex = this.Board.Children.IndexOf((Rectangle) sender);
CellIndex = c.CellIndex;
Console.WriteLine(CellIndex + " " + CellA[CellIndex].ItsAlive);
}
}
}
public List<Cell> CheckNeighbours(Cell c)
{
//NumOfColumns
//c.CellIndex
// 3 secations the ones above below and next to
List<Cell> NebourCell = new List<Cell> { };
NebourCell.Clear();
for (int i = -1; i < 2; i++)
{
try
{
NebourCell.Add(CellA[c.CellIndex - NumOfColumns - i]);
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Ignore");
}
}
for (int i = -1; i < 2; i += 2)
{
try
{
NebourCell.Add(CellA[c.CellIndex -i]);
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Ignore for middle index out of bounds");
}
}
for (int i = -1; i < 2; i++)
{
try
{
NebourCell.Add(CellA[c.CellIndex + NumOfColumns - i]);
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Ignore");
}
}
return NebourCell;
}
private void SetGrid(object sender, RoutedEventArgs e)
{
this.Board.Children.Clear();
this.Initializeboard();
}
private void Random(object sender, RoutedEventArgs e)
{
Random rand = new Random();
foreach (UIElement item in this.Board.Children)
{
Cell c = (item as Rectangle).GetBindingExpression(Rectangle.FillProperty).ResolvedSource as Cell;
if (rand.Next(0, 2) == 1)
{
c.ItsAlive = !c.ItsAlive;
}
}
}
private void Play(object sender, RoutedEventArgs e)
{
foreach (UIElement item in this.Board.Children)
{
}
}
private void Next(object sender, RoutedEventArgs e)
{
int Alive = 0;
foreach (Cell AllCell in CellA)
{
List<Cell> NebourCells = CheckNeighbours(CellA[AllCell.CellIndex]);
foreach ( Cell AllBebourCells in NebourCells)
{
Console.WriteLine(CellA[AllBebourCells.CellIndex].ItsAlive);
if (CellA[AllBebourCells.CellIndex].ItsAlive)
Alive++;
}
Console.WriteLine(Alive);
if (Alive < 2 || Alive > 3)
{
Console.WriteLine("It dies");
CellA[CellIndex].ItsAlive = false;
}
if (Alive == 2 || Alive == 3)
{
Console.WriteLine("Its lives on");
}
if (Alive == 3)
{
Console.WriteLine("Its alive");
CellA[CellIndex].ItsAlive = true;
}
//foreach (Cell item in NebourCell)
//{
// Rectangle ind = this.Board.Children[item.CellIndex] as Rectangle;
// ind.Fill = new SolidColorBrush(Colors.LightCyan);
//}
}
}
}
My problem i believe is in the Main Window cs and when you put three squares in a row and you go to next gen it is suppose to go vertical but mine just deletes the farthest one to the right i have been working on this for a while now i have been doing a lot of Debugging i have also debugged with break points i think it also is in the Next Button event Method but i could not find the problem and was wondering if anyone could assist me.

You have two problems with your Next method that I can see. First, you are not resetting your 'Alive' value between each cell that you process. So if the first cell you process has two alive neighbors, you Alive variable gets set to 2, and then if the next cell also has two alive neighbors, Alive is now set to 4!
Secondly, you are currently modifying the cells in place. This will lead to incorrect results, as if a certain cell dies, it will no longer be counted as alive for the processing of the cells next to it when it should be. You will need to create a temporary cell array that is a copy of CellA, and use the alive status of the cell in CellA but modify the status in the copy. Then at the end, set CellA to the copy.
Your new function should flow like this, in pseudocode
private void Next(object sender, RoutedEventArgs e)
{
Cell[] copy = copy of CellA
Loop over cells in copy
{
Alive = number of living neighbors of cell in CellA array
// apply Life logic here
copy[cell index].IsAlive = new status based on logic
}
CellA = copy
}

When you evolve your population (your CellA variable), you have to work on a copy of it. That is, you generate the new generation of cells into a newly allocated variable that you then assign to CellA only when you are done.

Related

Create and run a new progress every click on addTabItem and Start Button

I'm trying to create and start a new progressBar every click action on button start that exist in the new TabItem ( after clicking on the "+") , so the goal is to run multiple progressBar in the same time , these progressBar will be created on each new Tab Item , and will run when clicking on Start Button of ( each tab ) , so each tab item will have a Button and a progressBar .
How can I do this?
MainWindow.xaml.cs
private List<TabItem> _tabItems;
private TabItem _tabAdd;
ProgressBar pr;
public MainWindow()
{
try
{
InitializeComponent();
// initialize tabItem array
_tabItems = new List<TabItem>();
// add a tabItem with + in header
_tabAdd = new TabItem();
_tabAdd.Header = "+";
// tabAdd.MouseLeftButtonUp += new MouseButtonEventHandler(tabAdd_MouseLeftButtonUp);
_tabItems.Add(_tabAdd);
// add first tab
this.AddTabItem();
// bind tab control
tabDynamic.DataContext = _tabItems;
tabDynamic.SelectedIndex = 0;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private TabItem AddTabItem()
{
int count = _tabItems.Count;
pr = new ProgressBar();
pr.Width = 150;
pr.Height = 30;
// create new tab item
TabItem tab = new TabItem();
tab.Header = string.Format("Tab {0}", count);
tab.Name = string.Format("tab{0}", count);
tab.HeaderTemplate = tabDynamic.FindResource("TabHeader") as DataTemplate;
tab.MouseDoubleClick += new MouseButtonEventHandler(tab_MouseDoubleClick);
// add controls to tab item, this case I added just a textbox
StackPanel st = new StackPanel();
st.Width = 350;
st.Height = 350;
Button btn = new Button();
btn.Width = 150;
btn.Height = 30;
st.HorizontalAlignment = HorizontalAlignment.Center;
st.VerticalAlignment = VerticalAlignment.Center;
btn.Name = "txt";
btn.Content = "Start";
btn.Click += Btn_Click;
Border b = new Border();
b.BorderBrush = Brushes.Transparent;
b.BorderThickness = new Thickness(0, 0, 0, 10);
st.Children.Add(btn);
st.Children.Add(b);
st.Children.Add(pr);
st.Orientation = Orientation.Vertical;
tab.Content = st;
// insert tab item right before the last (+) tab item
_tabItems.Insert(count - 1, tab);
return tab;
}
private async void Btn_Click(object sender, RoutedEventArgs e)
{
await Task.Run(async () =>
{
this.Dispatcher.Invoke(new Action(async () => {
var progress = new Progress<int>(percent =>
{
pr.Value = percent;
});
pr.Value = 1;
int value = 90000;
await Task.Run(() => DoSomeWork(value, progress));
}), DispatcherPriority.ContextIdle);
});
}
public async Task DoSomeWork(int iterations, IProgress<int> progress)
{
int s = 0;
for (int i = 0; i < iterations; i++)
{
await Task.Run(() => {
s *= s * i;
progress.Report(i * 100 / iterations);
});
};
}
private void tabAdd_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// clear tab control binding
tabDynamic.DataContext = null;
TabItem tab = this.AddTabItem();
// bind tab control
tabDynamic.DataContext = _tabItems;
// select newly added tab item
tabDynamic.SelectedItem = tab;
}
private void tab_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
TabItem tab = sender as TabItem;
TabProperty dlg = new TabProperty();
// get existing header text
dlg.txtTitle.Text = tab.Header.ToString();
if (dlg.ShowDialog() == true)
{
// change header text
tab.Header = dlg.txtTitle.Text.Trim();
}
}
private void tabDynamic_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TabItem tab = tabDynamic.SelectedItem as TabItem;
if (tab == null) return;
if (tab.Equals(_tabAdd))
{
// clear tab control binding
tabDynamic.DataContext = null;
TabItem newTab = this.AddTabItem();
// bind tab control
tabDynamic.DataContext = _tabItems;
// select newly added tab item
tabDynamic.SelectedItem = newTab;
}
else
{
// your code here...
}
}
MainWindow.xaml
<Grid>
<TabControl Name="tabDynamic" ItemsSource="{Binding}" SelectionChanged="tabDynamic_SelectionChanged">
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" Padding="0" Click="btnDelete_Click" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
</Button>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
</DockPanel>
</DataTemplate>
<Style TargetType="TextBox">
<Setter Property="VerticalAlignment" Value="Stretch"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="AcceptsReturn" Value="True"></Setter>
<Setter Property="TextWrapping" Value="WrapWithOverflow"></Setter>
<Setter Property="MaxLines" Value="5000"></Setter>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"></Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"></Setter>
</Style>
</TabControl.Resources>
</TabControl>
</Grid>
TabProperty.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60px"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Right"></Setter>
</Style>
</Grid.Resources>
<Label Content="Tab Title:" Grid.Row="0" Grid.Column="0" Height="25" />
<TextBox Name="txtTitle" Grid.Row="0" Grid.Column="1" Height="25" />
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal">
<Button Content="OK" Name="btnOK" IsDefault="True" Height="23" Click="btnOK_Click"></Button>
<Button Content="Cancel" Name="btnCancel" IsCancel="True" Height="23"></Button>
</StackPanel>
</Grid>
TabProperty.xaml.cs
public partial class TabProperty : Window
{
public TabProperty()
{
InitializeComponent();
}
private void btnOK_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
}

Horizontal dynamic listbox with insert buttons

I would like to create an horizontal dynamic listbox:
A button is visible when the mouse is between two items.
<ListBox
MinHeight="32"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
SelectionMode="Extended"
HorizontalAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem>
<TextBlock><Run Text="C1" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C2" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C3" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C4" /></TextBlock>
</ListBoxItem>
</ListBox>
</ListBox>
Any suggestions, please?
Thank you
EDIT
private void myElement_MouseEnter(object sender, MouseEventArgs e)
{
//change button visibility
if (sender is Grid item)
{
if (item.DataContext is DataModel data)
{
System.Diagnostics.Debug.WriteLine("myElement_MouseEnter: " + data.TextValue);
}
var border1 = (Border)item.FindName("HitTestBorder1");
var border2 = (Border)item.FindName("HitTestBorder2");
if (border1 is Border)
{
var margin = border1.Margin;
margin.Left = -item.ActualWidth;
border1.Margin = margin;
}
if (border2 is Border)
{
var margin = border2.Margin;
margin.Left = item.ActualWidth;
border2.Margin = margin;
}
}
}
The problem is that the grid width is resized after mouse_enter... So, I don't get the overlay "effect".
Here is the solution.
I created a canvas. It contains 2 borders with button inside and ZPanel is setted very high (=1000) to be on top of everything.
I change the position of these borders on MouseEnter and MouseLeave events of ListBoxItem (see ItemTemplate).
XAML
<Border Padding="10">
<Canvas x:Name="supergrid" MouseLeave="supergrid_MouseLeave">
<Border x:Name="HitTestBorder1"
Panel.ZIndex="1000"
Background="Transparent"
BorderBrush="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderThickness="0,0,0,0" >
<Button x:Name="button1"
Visibility="Hidden"
Content="+"
Height="18" Width="18" FontSize="6" Background="Red"
Click="button1_Click"
/>
</Border>
<Border x:Name="HitTestBorder2"
Panel.ZIndex="1000"
Background="Transparent"
BorderBrush="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderThickness="1,1,1,1" >
<Button x:Name="button2"
Visibility="Hidden"
Content="+"
Height="18" Width="18" FontSize="6" Background="Green"
Click="button2_Click"
/>
</Border>
<ListBox x:Name="HorizontalListBox"
ItemsSource="{Binding DataModels}"
Margin="0,0,0,0"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Background="Yellow" MouseLeave="HorizontalListBox_MouseLeave">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<!--<Setter Property="MinWidth" Value="60" />
<Setter Property="MinHeight" Value="40" />-->
<Setter Property="Background" Value="Blue" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="myElement"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MouseEnter="myElement_MouseEnter"
MouseLeave="myElement_MouseLeave"
Background="White">
<TextBlock x:Name="myText"
Margin="10"
Text="{Binding TextValue}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Canvas>
</Border>
C#
public partial class MainWindow : Window
{
private DataModel currentDataModel;
public ObservableCollection<DataModel> DataModels { get; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.DataModels = new ObservableCollection<DataModel>();
this.DataModels.Add(new DataModel("Item1"));
this.DataModels.Add(new DataModel("Item2"));
this.DataModels.Add(new DataModel("SuperMegaHyperLong"));
this.DataModels.Add(new DataModel("Item3"));
this.DataModels.Add(new DataModel("Item4"));
this.DataModels.Add(new DataModel("123"));
this.DataModels.Add(new DataModel("Item5"));
}
private void myElement_MouseEnter(object sender, MouseEventArgs e)
{
updateOverlay((Grid)sender);
}
private void updateOverlay(Grid lbi)
{
if (lbi is Grid item) //the grid of listboxitem
{
if (item.DataContext is DataModel data)
{
Debug.WriteLine("myElement_MouseEnter: " + data.TextValue);
}
var myText = (TextBlock)item.FindName("myText");
if (myText is TextBlock)
{
Point relativePoint = myText.TransformToAncestor(supergrid)
.Transform(new Point(0, 0));
Debug.WriteLine("relativePoint: " + relativePoint.ToString());
//update left button position
double w = button1.ActualWidth;
double h = button1.ActualHeight;
double x = relativePoint.X - w / 2.0 - myText.Margin.Left;
double y = relativePoint.Y;
updateMargin(HitTestBorder1,
x,
x + w,
y + h,
y);
//update right button position
w = button2.ActualWidth;
h = button2.ActualHeight;
x = relativePoint.X - w / 2.0 - myText.Margin.Left;
x += item.ActualWidth;
y = relativePoint.Y;
updateMargin(HitTestBorder2,
x,
x + w,
y + h,
y);
//show the button
button1.Visibility = button2.Visibility = Visibility.Visible;
//the current item
if (myText.DataContext is DataModel dm)
{
this.currentDataModel = dm;
}
}
}
}
private void updateMargin(Border border, double left, double right, double bottom, double top)
{
//border = HitTestBorder2;
var margin = border.Margin;
margin.Left = left;
margin.Right = right;
margin.Top = top;
margin.Bottom = bottom;
border.Margin = margin;
Debug.WriteLine("updateMargin Left: " + left.ToString() + "Right: " + right.ToString());
}
private void myElement_MouseLeave(object sender, MouseEventArgs e)
{
//change button visibility
if (sender is Grid item)
{
if (item.DataContext is DataModel data)
{
System.Diagnostics.Debug.WriteLine("myElement_MouseLeave: " + data.TextValue);
}
//button1.Visibility = button2.Visibility = Visibility.Hidden;
}
}
private void HorizontalListBox_MouseLeave(object sender, MouseEventArgs e)
{
Debug.WriteLine("HorizontalListBox_MouseLeave");
//button1.Visibility = button2.Visibility = Visibility.Hidden;
}
private void supergrid_MouseLeave(object sender, MouseEventArgs e)
{
Debug.WriteLine("supergrid_MouseLeave: ");
button1.Visibility = button2.Visibility = Visibility.Hidden;
// => :D
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("LEFT INSERT");
int idx = this.DataModels.IndexOf(currentDataModel);
DataModel newDataModel = new DataModel($"Item{this.DataModels.Count}");
this.DataModels.Insert(idx, newDataModel);
button1.Visibility = button2.Visibility = Visibility.Hidden;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("RIGHT INSERT");
int idx = this.DataModels.IndexOf(currentDataModel);
this.DataModels.Insert(idx+1, new DataModel($"Item{this.DataModels.Count}"));
button1.Visibility = button2.Visibility = Visibility.Hidden;
}
}
public class DataModel
{
public string TextValue { get; set; }
public DataModel(string textValue)
{
this.TextValue = textValue;
}
}
As you can see, there is place for improvements (like select the new ListBoxItem). Disable the buttons when we drag, animation, delay, etc.

wpf c# datagrid GroupStyle - which rows are expanded

I have the following WPF DataGrid with GroupStyle. I need to now which rows are expanded when I have expanded/collapsed event.
I add:
Expanded="Expander_Process" Collapsed="Expander_Process"
but in the event function Expander_Process when I try to get the row
var row = DataGridRow.GetRowContainingElement(expander);
if (row == null)
then the row is null. So my question is: how can I know which rows are expanded in the datagrid?
<DataGrid x:Name="gvOptionChain" AutoGenerateColumns="False" FontWeight="Bold" Background="#FF262626" Foreground="White" Width="1509"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"
Margin="53,120,89,4.333" HorizontalContentAlignment="Right" SelectionChanged="gvOptionChain_SelectionChanged" VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.ScrollUnit ="Item" VirtualizingPanel.VirtualizationMode="Recycling" EnableRowVirtualization="True" EnableColumnVirtualization = "True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SelectionUnit="Cell" SelectedItem="{Binding SelectedItem, Mode=OneWay}" >
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="False" Foreground="#FFEEEEEE" Expanded="Expander_Process" Collapsed="Expander_Process" >
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupItem}}, Converter={StaticResource ResourceKey=groupToTitleConverter}}" />
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
var row = DataGridRow.GetRowContainingElement(expander);
if (row == null)
{
}
}
}
on the expander event - get the rows that expanded throu
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
and update a flag about the IsExpanded status of the row . this event happens many times for each expand - so I add timer to process the rows only once
private System.Timers.Timer ExpandTimer;
private bool bIsTimerOn = false;
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
for(int i = 0; i < Items1.Items.Count; i++)
{
OptScrtyData ScrtyData1 = (OptScrtyData)Items1.Items[i];
ScrtyData1.IsExpanded = expander.IsExpanded;
}
if (bIsTimerOn == false)
{
ExpandTimer.Start();
bIsTimerOn = true;
}
}
}
a better answer - activate the timer only if there is new expand status :
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
bool bNewStatus = false;
for(int i = 0; i < Items1.Items.Count; i++)
{
OptScrtyData ScrtyData1 = (OptScrtyData)Items1.Items[i];
if (bNewStatus == false)
{
bNewStatus = ScrtyData1.IsExpanded != expander.IsExpanded;
}
ScrtyData1.IsExpanded = expander.IsExpanded;
}
if (bNewStatus == true)
{
ExpandTimer.Start();
}
}
}

TabControl event SelectionChanged performed twice when change on identical item

In my program i create dynamically TabItems, and i can create a few the same items. Problem is when i am switching from one to second the same items. In this moment event:
private void TabCards_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.Source is TabControl)
{
...
}
}
run 2 times. I could give Stopwatch and block run this event twice time in a short period of time. But i dont think it is good solution.
Maybe have someone idea what can i do?
Edit
This is how i add item:
private void AddTabItemForCard(string cardName)
{
var style = (Style)resources.FindResource("Style" + cardName);
var tabItem = new TabItem
{
Style = style,
Header = cardName
};
TabCards.Items.Add(tabItem);
}
This is TabControl in xaml:
<TabControl x:Name="TabCards" Grid.Column="1" SelectionChanged="TabCards_SelectionChanged" >
</TabControl>
And style:
<Style x:Key="StyleWej" TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="AllowDrop" Value="True"/>
<EventSetter Event="PreviewMouseMove" Handler="TabItem_PreviewMouseMove"/>
<EventSetter Event="Drop" Handler="TabItem_Drop"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock TextWrapping="Wrap" Text="Adres(hex)" FontSize="15" Margin="10,10,396,444"/>
<ComboBox x:Name="AddressWej" HorizontalAlignment="Left" Margin="134,11,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem>30</ComboBoxItem>
<ComboBoxItem>31</ComboBoxItem>
<ComboBoxItem>32</ComboBoxItem>
</ComboBox>
<TextBlock TextWrapping="Wrap" Text="Wejscie 1:" FontSize="15" Margin="10,126,396,337"/>
<TextBlock TextWrapping="Wrap" Text="Wejscie 2:" FontSize="15" Margin="10,156,396,307"/>
<TextBox TextWrapping="Wrap" Text="Wejscie1" Width="120" Margin="134,129,231,331"/>
<TextBox TextWrapping="Wrap" Text="Wejscie2" Width="120" Margin="134,157,231,302"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Events:
private void TabItem_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (!(e.Source is TabItem tabItem))
return;
if (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed)
{
DragDrop.DoDragDrop(tabItem, tabItem, DragDropEffects.All);
}
}
private void TabItem_Drop(object sender, DragEventArgs e)
{
var tabItemTarget = e.Source as TabItem;
var tabItemSource = e.Data.GetData(typeof(TabItem)) as TabItem;
if (!tabItemTarget.Equals(tabItemSource))
{
var tabControl = tabItemTarget.Parent as TabControl;
int sourceIndex = tabControl.Items.IndexOf(tabItemSource);
int targetIndex = tabControl.Items.IndexOf(tabItemTarget);
tabControl.Items.Remove(tabItemSource);
tabControl.Items.Insert(targetIndex, tabItemSource);
tabControl.Items.Remove(tabItemTarget);
tabControl.Items.Insert(sourceIndex, tabItemTarget);
}
}
And in TabCards_SelectionChanged i check with TabItem is it and run:
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject rootObject) where T : DependencyObject
{
if (rootObject != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(rootObject); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(rootObject, i);
if (child != null && child is T)
yield return (T)child;
foreach (T childOfChild in FindVisualChildren<T>(child))
yield return childOfChild;
}
}
}
to find all TextBox from TabItem
enter image description here

Adding Avg, Min and Max Line to Scatter Chart

I am using a ScatterDataPoint chart in wpf,C#,.NET 3.5 and I have to add 3 horizontal lines for minimum, maximum and average value line that can be different depending on the values that I introduce.
Basically I take some values from a DB and I have to display them in a chart, below you have the basic code from which I started to create my application, but I do not know hoe to add those 3 lines.
here is the XAML code :
<Window.Resources>
<Style x:Key="BubbleDataPointStyle" TargetType="chartingToolkit:ScatterDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:ScatterDataPoint">
<Viewbox x:Name="viewbox">
<Ellipse Width="1px" Height="1px" Fill="Black"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="3"/>
<Setter Property="Height" Value="3"/>
</Style>
</Window.Resources>
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0,-28,0,28">
<Grid Height="921" Background="WhiteSmoke">
<chartingToolkit:Chart Name="lineChart" Title="Power Graph" Background="WhiteSmoke" Foreground="Black" VerticalAlignment="Top" Margin="16,36,20,0" Height="800" IsEnabled="True">
<chartingToolkit:ScatterSeries Title="Points" ItemsSource="{Binding}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" IsSelectionEnabled="True" DataPointStyle="{StaticResource BubbleDataPointStyle}">
<chartingToolkit:ScatterSeries.IndependentAxis>
<chartingToolkit:LinearAxis Orientation="X" Title="Time (Mins)" Interval="5" />
</chartingToolkit:ScatterSeries.IndependentAxis>
<chartingToolkit:ScatterSeries.DependentRangeAxis>
<chartingToolkit:LinearAxis Orientation="Y" Title="Lenght" x:Name="Yaxis"/>
</chartingToolkit:ScatterSeries.DependentRangeAxis>
</chartingToolkit:ScatterSeries>
</chartingToolkit:Chart>
</Grid>
</ScrollViewer>
</Grid>
</Window>
Here is the code behind, now it just generates a random point:
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();
ObservableCollection<KeyValuePair<double, double>> Power = new ObservableCollection<KeyValuePair<double, double>>();
public MainWindow()
`enter code here` {
InitializeComponent();
showColumnChart();
timer.Interval = new TimeSpan(0,0,0,0,1); // per 5 seconds, you could change it
timer.Tick += new EventHandler(timer_Tick);
timer.IsEnabled = true;
}
double i = 1;
Random random = new Random();
void timer_Tick(object sender, EventArgs e)
{
Power.Add(new KeyValuePair<double, double>(i, random.NextDouble()));
i += 1;
if(Power.Count==500)
{
timer.IsEnabled = false;
}
}
private void showColumnChart()
{
lineChart.DataContext = Power;
}
}
}
Normally you would use a StripLine for that, but the toolkit doesn't seem to have one. So use an extra LineSeries instead:
XAML:
<Window x:Class="WpfApplication336.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
xmlns:local="clr-namespace:WpfApplication336"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="BubbleDataPointStyle" TargetType="chartingToolkit:ScatterDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:ScatterDataPoint">
<Viewbox x:Name="viewbox">
<Ellipse Width="1px" Height="1px" Fill="Black"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="3"/>
<Setter Property="Height" Value="3"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<chartingToolkit:Chart Grid.Row="0" Name="lineChart" Title="Power Graph" Background="WhiteSmoke" Foreground="Black" IsEnabled="True">
<chartingToolkit:Chart.Series>
<chartingToolkit:ScatterSeries Title="Points" ItemsSource="{Binding Power}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" IsSelectionEnabled="True" DataPointStyle="{StaticResource BubbleDataPointStyle}"/>
<chartingToolkit:LineSeries Title="Average" ItemsSource="{Binding PowerAvg}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" />
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>
<Button Grid.Row="1" Click="Button_Click">START</Button>
</Grid>
ViewModel:
public class MyViewModel
{
public ObservableCollection<KeyValuePair<double, double>> Power { get; set; }
public ObservableCollection<KeyValuePair<double, double>> PowerAvg { get; set; }
public MyViewModel()
{
Power = new ObservableCollection<KeyValuePair<double, double>>();
PowerAvg = new ObservableCollection<KeyValuePair<double, double>>();
}
public void Add(double x, double y)
{
Power.Add(new KeyValuePair<double, double>(x, y));
double xmin = Power.Min(kvp => kvp.Key);
double xmax = Power.Max(kvp => kvp.Key);
double ymin = Power.Min(kvp => kvp.Value);
double ymax = Power.Max(kvp => kvp.Value);
double yavg = Power.Average(kvp => kvp.Value);
PowerAvg.Clear();
PowerAvg.Add(new KeyValuePair<double, double>(xmin, yavg));
PowerAvg.Add(new KeyValuePair<double, double>(xmax, yavg));
}
}
MainWindow:
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();
MyViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new MyViewModel();
DataContext = vm;
//showColumnChart();
timer.Interval = new TimeSpan(0, 0, 0, 0, 1); // per 5 seconds, you could change it
timer.Tick += new EventHandler(timer_Tick);
//timer.IsEnabled = true;
}
double i = 1;
Random random = new Random();
void timer_Tick(object sender, EventArgs e)
{
vm.Add(i, random.NextDouble());
i += 1;
if (vm.Power.Count == 250)
{
timer.Stop();
}
}
private void showColumnChart()
{
lineChart.DataContext = vm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
timer.Start();
}
}
You can easily add extra LineSeries for min and max just like we did for the average.

Categories