Very new to WPF. I am trying to achieve something relatively simple that is proving to be difficult.
Basically, I want to add an Item to my List Box. The List Box is created in my LiveView xaml/class, but I want to update the contents of the List Box when I push a button in my SettingsView Class.
SettingsView class:
public partial class SettingsView : UserControl
{
public SettingsView()
{
InitializeComponent();
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
var myLiveView = new LiveView();
myLiveView.updateListBox();
}
}
LiveView class:
public partial class LiveView : UserControl
{
public LiveView()
{
InitializeComponent();
}
public void updateListBox()
{
CommentListBox.Items.Add("Another item");
}
}
If I do the following, the code works and an item is sucessfully added to my list on startup.
public partial class LiveView : UserControl
{
public LiveView()
{
InitializeComponent();
CommentListBox.Items.Add("Another item");
}
}
Why can I only update the UI inside the Liveview class()? What is the right way to go about this? how can I update my ListBox from another class/view? The instance that I'm creating of LiveView doesn't appear to actually do anything. Any help would be much appreiated, thank you.
When you create a new instance of user control so it is empty now, to handle this please check the delegates and events in both the user controls with respect parent and child navigation.
Fixed using MVVM which made this whole process much easier.
Related
I am creating an application using WinForms. I have panel in which I show a user control. Inside this user control I have a button. When I click the button, I want to clear the panel and show a different user control. I am trying to do that using the following code:
private void btnCreateOffer_Click(object sender, EventArgs e)
{
var myControl = new WindowsFormsDemo.View.CreateOffer();
MockUpForm.panMain.Controls.Clear();
MockUpForm.panMain.Controls.Add(myControl);
}
This works from the buttons placed directly in the parrent form, but when I use in inside the user control, it says:
'MockUpForm.panMain' is inaccessible due to its protection level
I suppose it has something to do with private/public classes. But I would rather have the "correct" solution, as opposed to just changing everything to public.
Any suggestions on how this is usually done?
Solution 1 (ugly):
Make panMain public in the designer:
Solution 2 (somewhat better):
Provide public methods to achieve such tasks safely:
// MockUpForm code:
public void ClearPanelControls()
{
panMain.Controls.Clear();
}
public void AddControlToPanel(Control c)
{
panMain.Controls.Add(c);
}
And then call these methods instead of publishing the full panel, which makes possible for example to dispose the whole panel and such things...
To access parent form's control from UserControl You can use delegate and event
something like this....
Windows Form (Parent Form) Code....
public Form1()
{
InitializeComponent();
userControl1.CreateOffer += UserControl1_CreateOffer;
}
private void UserControl1_CreateOffer()
{
var myControl = new WindowsFormsDemo.View.CreateOffer();
this.panMain.Controls.Clear();
this.panMain.Controls.Add(myControl);
}
User Control Code...
internal delegate void CreateOfferDelegate();
internal event CreateOfferDelegate CreateOffer;
public UserControl1()
{
InitializeComponent();
}
private void btnCreateOffer_Click(object sender, EventArgs e)
{
CreateOffer();
}
I have a main form with a tab control on it. The tabs get populated by adding panels from other forms within the solution. One of these panels has some code that will pop up an options window. I want that window to align itself to the top right hand side of the main form. To do this I need the location and size of the main form. However, I cannot seem to access any property that will tell one of the panels what the location of that main form is.
I've tried things like this.Parent, this.ParentForm and this.GetContainerControl(). They all return null.
Any ideas?
Addendum
//Code for the main form:
namespace WinAlignTest {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
tabControl1.TabPages[0].Controls.Add(new SomeApplication().panel1);
}
}
}
//Code that shows the option window
namespace WinAlignTest {
public partial class SomeApplication : Form {
private ApplicationOptions Options;
public SomeApplication() {
InitializeComponent();
Options = new ApplicationOptions();
}
private void button1_Click(object sender, EventArgs e) {
Options.Show();
//This will always move the location to {0,0}
Options.Location = new Point(base.Location.X,base.Location.Y);
}
}
}
I'm confused, you seem to be adding a panel which belongs to SomeApplication to Form1. I would suggest you actually make SomeApplication a UserControl instead of a form:
//Code for the main form:
namespace WinAlignTest {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
tabControl1.TabPages[0].Controls.Add(new SomeApplication());
}
}
}
//Code that shows the option window
namespace WinAlignTest {
public partial class SomeApplication : UserControl {
private ApplicationOptions Options;
public SomeApplication() {
InitializeComponent();
Options = new ApplicationOptions();
}
private void button1_Click(object sender, EventArgs e) {
Options.Show();
// You might need to use PointToScreen here
Options.Location = this.Location;
}
}
}
Check out
Application.OpenForms
That should give you access to what you want.
the base identifier accesses a parent element.
Two possible problems: First, your constructors don't explicitly extend your base constructor. it would look like this:
public Form1():base(){}
I still recommend making getter methods in the Form1 class. It would look something like this:
public int Form1Location
{
get{return /*FormLocation*/}
}
and call it from WinAlignTest
Let me know if this works.
Just trying to figure out an easy way to either pass or share some data between the main window and a dialog box.
I've got a collection of variables in my main window that I want to pass to a dialog box so that they can be edited.
The way I've done it now, is I pass in the list to the constructor of the dialog box:
private void Button_Click(object sender, RoutedEventArgs e)
{
var window = new VariablesWindow(_templateVariables);
window.Owner = this;
window.ShowDialog();
if(window.DialogResult == true)
_templateVariables = new List<Variable>(window.Variables);
}
And then in there, I guess I need to deep-copy the list,
public partial class VariablesWindow : Window
{
public ObservableCollection<Variable> Variables { get; set; }
public VariablesWindow(IEnumerable<Variable> vars)
{
Variables = new ObservableCollection<Variable>(vars);
// ...
So that when they're edited, it doesn't get reflected back in the main window until the user actually hits "Save".
Is that the correct approach? If so, is there an easy way to deep-copy an ObservableCollection? Because as it stands now, I think my Variables are being modified because it's only doing a shallow-copy.
I think you are indeed following the right approach here, but you need to make a deep copy of your ObservableCollection. To do so, make sure that your class 'Variable' is Clonable (try to implement ICloneable)
foreach(var item in vars)
{
Variables.Add((Variable)item.Clone());
}
I would use events to communicate between the two forms if you want the main form to update while the dialog is open. Expose an event ("ItemAdded" or whatever) from your dialog class that the main form can handle. When that event is fired, update the main form as needed.
This extension method might help somebody:
public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> collection) where T : ICloneable
{
return collection.Select(x => (T) x.Clone());
}
It simplifies my dialog window slightly:
public partial class VariablesWindow : Window
{
public ObservableCollection<TemplateVariable> Variables { get; private set; }
public VariablesWindow(IEnumerable<TemplateVariable> vars)
{
Variables = new ObservableCollection<TemplateVariable>(vars.DeepCopy());
I have 2 forms, in form 2 i have button that adds Fname and Lname items, when i press this button . I want to see those items in ListView that is in Form1
thank's in advance
This sounds like a case where you'd want a presenter class which was responsible for populating and maintaining both forms with the same data. Then you could expose the button click event from your form, and hook your presenter up to handle it. From here you can choose to update whatever form you want, in whatever way you want.
For example:
public class MyForm1 : Form, IMyForm1
{
... // Bunch of other stuff
public event<EventHandler> onButtonClick;
}
public class MyPresenter
{
public static void Main()
{
... // Other stuff
myForm1.onButtonClick += new EventHandler<EventArgs>(ButtonHandler);
}
private void ButtonHandler(object sender, EventArgs e)
{
// Add item to form1
...
// Add item to form2. Eg:
form2.AddListItem(...);
}
}
The idea of having a presenter is that you have a centralised location for logic relating to those forms, so the forms can be as thin as possible, and they don't even have to know about each other.
Hope that helps. Give me a shout if I've missed the point or need to clarify anything I've said.
You will need to either create a public method in Form1 to add the values, or expose the listview as public from Form1.
Maybe something like
public void AddToListView(string fname, string lname)
{
//add values here to ListView
}
I have added an EventHandler for the Click-event to a picturebox but on runtime this handler is never called (the debugger shows me that it is added to the control directly but when I click on the picturebox nothing happens).
I assume it has something to do with my inheritance. I have a usercontrol called AbstractPage (its not really abstract since the designer doesnt like that) which only consists of a heading and this picturebox but it provides quite some functions the actual pages rely on.
#region Constructor
public AbstractPage()
{
InitializeComponent();
lblHeading.Text = PageName;
picLock.Click += new EventHandler(picLock_Click);
}
#endregion
#region Events
void picLock_Click(object sender, EventArgs e)
{
...do some stuff
}
#endregion
The page implementations just inherit this class and add their controls and behavior. We recently figured out that subclassing UserControl is not performant and we lose some performance there, but its the best way to do it (I dont want to c&p function for 25 pages and maintain them).
My pageA looks like this
public partial class PageA : AbstractPage
{
#region Constructor
public PageA()
{
// I dont call the base explicitely since it is the
// standard constructor and this always calls the base
InitializeComponent();
}
#endregion
public override string PageName
{
get { return "A"; }
}
public override void BindData(BindingSource dataToBind)
{
...
}
Anyway, the picLock_Click is never called and I dont know why?
The pages are all put into a PageControl which consists of a TreeView and a TabContainer where the pages are put once I call addPage(IPage)
public partial class PageControl {
...
protected virtual void AddPages()
{
AddPage(new PageA());
AddPage(new PageD());
AddPage(new PageC());
...
}
protected void AddPage(IPage page)
{
put pagename to treeview and enable selection handling
add page to the tabcontainer
}
Thanks in advance
If I understand your problem correctly, this worked for me out of the box (using VS2k8). My code:
public partial class BaseUserControl : UserControl
{
public BaseUserControl()
{
InitializeComponent(); //event hooked here
}
private void showMsgBox_Click(object sender, EventArgs e)
{
MessageBox.Show("Button clicked");
}
}
public partial class TestUserControl : BaseUserControl
{
public TestUserControl()
{
InitializeComponent();
}
}
I moved the TestUserControl to a form, clicked the button and got the message box as expected. Can you paste some more code, e.g. how do you use your AbstractPage?
I found the problem. We are using the Infragistics WinForms but in that case I used the standard picturebox. I replaced it with the UltraPictureBox and now it works.