C
C#7mo ago
LSiper

WPF how to add data from 2 list entries to a different window that uses a CustomContol

First time using C# and WPF. Currently my application has 2 windows - the main window and a secondary window to add data to the main window. - In the secondary window you add data via 2 textboxes, once you fill both textboxes and click a button the input gets stored in 2 different list entries and can then further add more entries or delete specific entires or all entries using an ObservableCollection. - the place I want all of the entries to be stored once the user is done adding entries is in the main window which uses a customcontrol that also has 2 different list entries. Sorry if this is too complicated or if the explanation doesn't make much sense, maybe I just have the wrong approach, but either way how do you add all of the elements from a list entry to a list entry that is on a different window?
57 Replies
LSiper
LSiperOP7mo ago
Code behind from the second window:
c#
using System.Collections;
using System.Windows;
using WpfApp1.Classes;
using System.Windows.Controls;
using System.Collections.ObjectModel;

namespace WpfApp1
{
public partial class AddFactoryMeasurements : Window
{
public AddFactoryMeasurements()
{
DataContext = this;
unitEntries = new ObservableCollection<string>();
measurementEntries = new ObservableCollection<string>();
InitializeComponent();
}

private ObservableCollection<string> unitEntries;

public ObservableCollection<string> UnitEntries
{
get { return unitEntries; }
set { unitEntries = value; }
}

private ObservableCollection<string> measurementEntries;

public ObservableCollection<string> MeasurementEntries
{
get { return measurementEntries; }
set { measurementEntries = value; }
}




private void btnAddFactory_Click(object sender, RoutedEventArgs e)
{
if (DoubleValidation.DoubleValidateInput(Measurement.txtInput.Text, Units.txtInput.Text) == true)
{
unitEntries.Add(Units.txtInput.Text);
measurementEntries.Add(Measurement.txtInput.Text);
};
}
c#
using System.Collections;
using System.Windows;
using WpfApp1.Classes;
using System.Windows.Controls;
using System.Collections.ObjectModel;

namespace WpfApp1
{
public partial class AddFactoryMeasurements : Window
{
public AddFactoryMeasurements()
{
DataContext = this;
unitEntries = new ObservableCollection<string>();
measurementEntries = new ObservableCollection<string>();
InitializeComponent();
}

private ObservableCollection<string> unitEntries;

public ObservableCollection<string> UnitEntries
{
get { return unitEntries; }
set { unitEntries = value; }
}

private ObservableCollection<string> measurementEntries;

public ObservableCollection<string> MeasurementEntries
{
get { return measurementEntries; }
set { measurementEntries = value; }
}




private void btnAddFactory_Click(object sender, RoutedEventArgs e)
{
if (DoubleValidation.DoubleValidateInput(Measurement.txtInput.Text, Units.txtInput.Text) == true)
{
unitEntries.Add(Units.txtInput.Text);
measurementEntries.Add(Measurement.txtInput.Text);
};
}
c#
private void btnClear_Click(object sender, RoutedEventArgs e)
{
MeasurementEntries.Clear();
UnitEntries.Clear();
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
if(lvEntries_TempMeasurements.SelectedItem != null)
{
MessageBoxResult result = MessageBox.Show("Are You sure you want to delete this item?", "Delete?",
MessageBoxButton.YesNo, MessageBoxImage.Question);

if (result == MessageBoxResult.Yes)
{
string selectedItem = (string)lvEntries_TempMeasurements.SelectedItem;
var index = measurementEntries.IndexOf(selectedItem);

MeasurementEntries.Remove(selectedItem);
UnitEntries.RemoveAt(index);
}

}
}
private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
; // how to make this???
}

private void lvEntries_TempUnits_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
}
}
c#
private void btnClear_Click(object sender, RoutedEventArgs e)
{
MeasurementEntries.Clear();
UnitEntries.Clear();
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
if(lvEntries_TempMeasurements.SelectedItem != null)
{
MessageBoxResult result = MessageBox.Show("Are You sure you want to delete this item?", "Delete?",
MessageBoxButton.YesNo, MessageBoxImage.Question);

if (result == MessageBoxResult.Yes)
{
string selectedItem = (string)lvEntries_TempMeasurements.SelectedItem;
var index = measurementEntries.IndexOf(selectedItem);

MeasurementEntries.Remove(selectedItem);
UnitEntries.RemoveAt(index);
}

}
}
private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
; // how to make this???
}

private void lvEntries_TempUnits_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
}
}
"btnConfirm_Click" is the button that is trying to transfer the data from the list entries to the mainwindow and into the CustomControl list entries whilist also closing the second window and clearing the lists (in the second window)
leowest
leowest7mo ago
so MeasurementEntries and UnitEntries are supposed to be shared between the 2 windows?
LSiper
LSiperOP7mo ago
One thing I am thinking about is if a seperate file is made that stores all of that information and just have the Confirm Click button send all of that info to the file and have the mainwindow somehow read that file and get the information that way. yes
leowest
leowest7mo ago
side note, u dont need to do all thise for observablecollection:
private ObservableCollection<string> unitEntries;

public ObservableCollection<string> UnitEntries
{
get { return unitEntries; }
set { unitEntries = value; }
}

private ObservableCollection<string> measurementEntries;

public ObservableCollection<string> MeasurementEntries
{
get { return measurementEntries; }
set { measurementEntries = value; }
}
private ObservableCollection<string> unitEntries;

public ObservableCollection<string> UnitEntries
{
get { return unitEntries; }
set { unitEntries = value; }
}

private ObservableCollection<string> measurementEntries;

public ObservableCollection<string> MeasurementEntries
{
get { return measurementEntries; }
set { measurementEntries = value; }
}
Just
public ObservableCollection<string> UnitEntries { get; set; }
public ObservableCollection<string> MeasurementEntries { get; set; }
public ObservableCollection<string> UnitEntries { get; set; }
public ObservableCollection<string> MeasurementEntries { get; set; }
well then what u would do is simple, you would use dependency injection to pass a reference of your windowA to windowB i.e.:
private readonly WindowA _parent;
public AddFactoryMeasurements(WindowA parent)
{
_parent = parent;
InitializeComponent();
DataContext = this;
}
private readonly WindowA _parent;
public AddFactoryMeasurements(WindowA parent)
{
_parent = parent;
InitializeComponent();
DataContext = this;
}
no you have both MeasurementEntries and UnitEntries accessible within the 2nd window does that make sense to u?
LSiper
LSiperOP7mo ago
kind of - trying to make it work and understand will ask for help if needed
leowest
leowest7mo ago
ok, just in case u access those properties via _parent, i.e.:
_parent.MeasurementEntries.Remove(selectedItem);
_parent.MeasurementEntries.Remove(selectedItem);
LSiper
LSiperOP7mo ago
okay managed to finally make it work with some more help. thanks a lot!
leowest
leowest7mo ago
and how u ended up doing it? care to share some code so I can see what u did and possible advise further
LSiper
LSiperOP7mo ago
changes in the secondwindow:
c#
private readonly MainWindow _parent;
public AddFactoryMeasurements(MainWindow parent)
{
DataContext = this;
_parent = parent;
unitEntries = new ObservableCollection<string>();
measurementEntries = new ObservableCollection<string>();
InitializeComponent();
}
c#
private readonly MainWindow _parent;
public AddFactoryMeasurements(MainWindow parent)
{
DataContext = this;
_parent = parent;
unitEntries = new ObservableCollection<string>();
measurementEntries = new ObservableCollection<string>();
InitializeComponent();
}
+
c#
private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
this._parent.AddItems(UnitEntries, MeasurementEntries);
this.Close();
}
c#
private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
this._parent.AddItems(UnitEntries, MeasurementEntries);
this.Close();
}
relevant code in the main window:
c#
private void btnAddFactory_Click(object sender, EventArgs e)
{
AddFactoryMeasurements objAddFactoryMeasurements = new AddFactoryMeasurements(this);
objAddFactoryMeasurements.Owner = this;
objAddFactoryMeasurements.ShowDialog();
}

private string GetElementAtIndex(ObservableCollection<string> collection, int index)
{
return collection[index];
}

public void AddItems(ObservableCollection<string> Units, ObservableCollection<string> Measurements)
{
var total = Measurements.Count;
for (int index = 0; index < total; index++)
{
string currentUnit = GetElementAtIndex(Units, index);
string currentMeasurement = GetElementAtIndex(Measurements, index);
FactoryMeasurementsList.lvEntries_PlaceholderUnits.Items.Add(currentUnit);
FactoryMeasurementsList.lvEntries_PlaceholderMeasurements.Items.Add(currentMeasurement);
}
}
c#
private void btnAddFactory_Click(object sender, EventArgs e)
{
AddFactoryMeasurements objAddFactoryMeasurements = new AddFactoryMeasurements(this);
objAddFactoryMeasurements.Owner = this;
objAddFactoryMeasurements.ShowDialog();
}

private string GetElementAtIndex(ObservableCollection<string> collection, int index)
{
return collection[index];
}

public void AddItems(ObservableCollection<string> Units, ObservableCollection<string> Measurements)
{
var total = Measurements.Count;
for (int index = 0; index < total; index++)
{
string currentUnit = GetElementAtIndex(Units, index);
string currentMeasurement = GetElementAtIndex(Measurements, index);
FactoryMeasurementsList.lvEntries_PlaceholderUnits.Items.Add(currentUnit);
FactoryMeasurementsList.lvEntries_PlaceholderMeasurements.Items.Add(currentMeasurement);
}
}
For some reason I couldn't add items in the CustomControl from the second window even when directly setting a specific value from the code itself, but instead pushing the observable collections into the mainwindow worked perfectly. definetly not the best or cleanest execution out there, but am glad it works
leowest
leowest7mo ago
Would u be able to show me what the 2 windows look like? and their xaml
LSiper
LSiperOP7mo ago
sure I'll post how they look right now (not pretty).
leowest
leowest7mo ago
its ok, I just want to have an idea so I can try to reproduce the issue it should work without having to do what u did
LSiper
LSiperOP7mo ago
main window:
No description
LSiper
LSiperOP7mo ago
the part that uses the custom control is the factory measurements and existing measurements (not implemented yet) under new measurements there are 2 seperate list views
leowest
leowest7mo ago
ah so its not multiple window
LSiper
LSiperOP7mo ago
ye
leowest
leowest7mo ago
its window and usercontrol?
LSiper
LSiperOP7mo ago
the project has 2 windows and 2 user controls right now and a class file for input validation
leowest
leowest7mo ago
ok but what u posted all happens in the above window + usercontrol right?
LSiper
LSiperOP7mo ago
no
LSiper
LSiperOP7mo ago
this is the second window again uses 2 different list view rather than the control
No description
leowest
leowest7mo ago
mmm ok
LSiper
LSiperOP7mo ago
should start making sense now
leowest
leowest7mo ago
yeah I suppose, u add multiple combination on the last window confirm and it should move to previous window
LSiper
LSiperOP7mo ago
yes!
leowest
leowest7mo ago
how are u binding the list you're using ItemsSource? if u could add directly to the list on the previous window would that be better? actually I dont even think u need that second window
LSiper
LSiperOP7mo ago
I can make it all in the mainwindow but UI would get too cluttered?
leowest
leowest7mo ago
can u show me hte first window populate with some info? I will show u an idea
LSiper
LSiperOP7mo ago
No description
LSiper
LSiperOP7mo ago
note that the existing will expand up once it has info as well definetly don't mind redesigning the application. also note that "New Measurements" and the listviews underneath it are completely seperate from the measurements that are "saved" to the left
leowest
leowest7mo ago
does anything else goes under the right side on the white space
LSiper
LSiperOP7mo ago
on the right (bottom right-ish) I havent added the button that basically does all of the math and spits you out the result the user is looking for based on all of the lists on a different 3rd window.
LSiper
LSiperOP7mo ago
that would currently be
No description
leowest
leowest7mo ago
so by adding a datatemplate
leowest
leowest7mo ago
u could do something like this
No description
leowest
leowest7mo ago
and then u could remove this part
leowest
leowest7mo ago
No description
leowest
leowest7mo ago
and have the form the there with add and clear all honestly I am trying to understand why do u need to collect unit/measurements there and then u have it again on the right can't it be a unified process?
LSiper
LSiperOP7mo ago
my idea is that everything on the left will eventually have it's save file or something along those lines so that when you open the program you have everything that is left stored there. The add factory measurements can only add measurements or do nothing once that window is closed. I do plan on adding delete buttons to the usercontrol like you showed.
leowest
leowest7mo ago
so you're saying the damage and disc are side settings u dont want to save these are settings u want to use the units/measurements with when u fill then
LSiper
LSiperOP7mo ago
The whole idea of the application is let's you have 2 planks that are different width that would be your "Factory Measurements" and you want to cut the planks into smaller pieces that would be your "New Measurements". When you hit the "optimize" button that doesn't exist currently. The program's purpose is to show/tell the user from which plank to cut what smaller pieces so that they waste less of the planks. the side damage and disc settings are only there to setup and get an accurate result. what I want to be saved in the future is let's say you have like 1meter left from one of the planks in the above example that 1 meter leftover would move into the "Existing" usercontrol and if you had way more planks to begin with and some of them weren't used at all in the previously they would stay under "Factory"
leowest
leowest7mo ago
I see well in this case I would just make that left side a navigation menu and have them both be pages
LSiper
LSiperOP7mo ago
that is a good idea
leowest
leowest7mo ago
this way u have more space to work with less bloat or even a tabcontrol so back to the problem are u using ItemsSource in your Measurements Factory list? also I just realize ur using strings oh no :Smadge:
LSiper
LSiperOP7mo ago
mainwindow.
<UserControls:ListMeasurements Grid.Row="1" x:Name="FactoryMeasurementsList" Placeholder="Factory" VerticalAlignment="Top"/>
<UserControls:ListMeasurements Grid.Row="1" x:Name="FactoryMeasurementsList" Placeholder="Factory" VerticalAlignment="Top"/>
using itemsSource binding on the second window
leowest
leowest7mo ago
public class UnitMeasurement
{
public int Unit {get;set;}
public int Measurement {get;set;}
}
public class UnitMeasurement
{
public int Unit {get;set;}
public int Measurement {get;set;}
}
Then u can just have:
public ObservableCollection<UnitMeasurement> Entries { get; set; }
public ObservableCollection<UnitMeasurement> Entries { get; set; }
then u would just have say
<ListBox ItemsSource="{Binding Entries}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Unit}" />
<TextBlock Grid.Column="1" Text="{Binding Measurement}" />
<Button Grid.Column="2" Content="Delete" HorizontalAlignment="Right"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox ItemsSource="{Binding Entries}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Unit}" />
<TextBlock Grid.Column="1" Text="{Binding Measurement}" />
<Button Grid.Column="2" Content="Delete" HorizontalAlignment="Right"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
what is the benefit of all this? - Observable collection takes care of updating the items in the listbox u dont have to manually add items to the list - You can easily customize what the rows look like or what data it presents You can further set a SelectedItem binding it u need to. in the above all u would do to add or remove an item would be For example:
Entries.Add(new UnitMeasurement { Unit = 10, Measurement = 20 });
Entries.Add(new UnitMeasurement { Unit = 10, Measurement = 20 });
doesn't need to be a listbox can be done with a datagrid or listview as well then _parent.Entries should work as intended from the 2nd window and would remove/add items on the list behind it u could even bind it to both lists and it would synchronize both althou I find it unecessary and that is why I was trying to understand your layout because I feel u would get away without needing that 2nd window u have so much space in the left side
LSiper
LSiperOP7mo ago
Combining all of this with a navigation bar will definitely be better... not quite familiar with Listbox or datagrids yet
leowest
leowest7mo ago
it not much different from your listview in terms of being able to set the datatemplate etc
LSiper
LSiperOP7mo ago
currently can't decide on if I should do the redesign first and then try to make the program work or if I should just try to get the program to work and then redeisgn redesign should be better? since if I also show the results also in the mainwindow I wouldn't have to worry about accessing elements and such.
leowest
leowest7mo ago
well u definitively should redesign to use bindings and command where needed and avoid code behind i.e.: x:Name etc this will help u also learn towards entering MVVM pattern which is very helpful and organizational for UI Frameworks what u can do in xaml do in xaml etc
LSiper
LSiperOP7mo ago
i see If you want I can post here the redesign here once it's done will take some time tho
leowest
leowest7mo ago
sure if u have questions feel free to ask
LSiper
LSiperOP7mo ago
okay, thank you so much!
leowest
leowest7mo ago
leowest
leowest7mo ago
essentially what I explained above with the observable collection, + the class and itemssource
LSiper
LSiperOP7mo ago
how did you manage to make the delete button to delete the specific Entry? I made the listbox inside a usercontrol that will be used in other usercontrols that will be displayed on the main window when you click a button on the navbar
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<TextBlock Text="Unit:" FontSize="68" FontWeight="Thin" Margin="8"/>
<TextBlock Grid.Column="1" Text="Measurement:" FontSize="68" FontWeight="Thin" Margin="8"/>

<ListBox x:Name="EntryList" ItemsSource="{Binding Entries}" Grid.Row="1" Grid.ColumnSpan="2">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,8" Width="{Binding ElementName=EntryList, Path=ActualWidth}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" MinWidth="115"/>
<ColumnDefinition Width="45*" MinWidth="195"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Unit}" Margin="0,0,16,0" FontSize="64"/>
<TextBlock Grid.Column="1" Text="{Binding Measurement}" Margin="0,0,16,0" FontSize="64"/>
<Button Grid.Column="2" Content="Delete" HorizontalAlignment="Right" Margin="0,0,16,0" FontSize="56" Click="btnDelete_Click"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<TextBlock Text="Unit:" FontSize="68" FontWeight="Thin" Margin="8"/>
<TextBlock Grid.Column="1" Text="Measurement:" FontSize="68" FontWeight="Thin" Margin="8"/>

<ListBox x:Name="EntryList" ItemsSource="{Binding Entries}" Grid.Row="1" Grid.ColumnSpan="2">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,8" Width="{Binding ElementName=EntryList, Path=ActualWidth}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" MinWidth="115"/>
<ColumnDefinition Width="45*" MinWidth="195"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Unit}" Margin="0,0,16,0" FontSize="64"/>
<TextBlock Grid.Column="1" Text="{Binding Measurement}" Margin="0,0,16,0" FontSize="64"/>
<Button Grid.Column="2" Content="Delete" HorizontalAlignment="Right" Margin="0,0,16,0" FontSize="56" Click="btnDelete_Click"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
also I get "Object reference not set to an instance of an object" error when trying to use the usercontrol
leowest
leowest7mo ago
I use Command and CommandParameter
<Button Grid.Column="2" Content="Delete" HorizontalAlignment="Right" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=DataContext.RemoveCommand}" CommandParameter="{Binding .}"/>
<Button Grid.Column="2" Content="Delete" HorizontalAlignment="Right" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=DataContext.RemoveCommand}" CommandParameter="{Binding .}"/>
Want results from more Discord servers?
Add your server