Change label text from different header file, Visual C++ 2010? - winforms

I am using Visual C++ 2010 Express. I have a form (Form1.h) that contains a button (btn1) and a label (label1).
When I click the button, I would like to call a function from a different header file (testing.h) that will then proceed to change the text in the label.
What I have is something like this...
Form1.h
#include "testing.h"
... standard form code generated by Visual Studio
private: System::Windows::Forms::Label^ label1;
...
private: System::Void btn1_Click(System::Object^ sender, System::EventArgs^ e) {
testfunc1();
}
};
Where testing.h is something like...
#ifndef _TESTING_FUNCS
#define _TESTING_FUNCS
void testfunc1(){
label1->Text = "Text has been changed from outside.";
}
#endif
When I try to compile and run it, I get errors saying that 'label1' is an undeclared identifier (within testing.h), and an error referring to the "left of '->Text' must point to class/struct/..."
I am new to C++ and usually use Java, so there are a few new things for me here. To me, there are two obvious options:
1) Pass the label to the function as an argument
2) Somehow access the label from the testing.h header file, sans reference
But I'm not really sure how to do either.

The label is a private variable of a class and just like in Java is not accessible from the outside, especially not in static context. You can pass the label, or you can create an accessor function in your Form and pass the whole form.
Example for passing the label:
void testfunc1(System::Windows::Forms::Label^ someLabel)
{
someLabel->Text = "Text has been changed from outside.";
}
Calling it:
System::Void btn1_Click(System::Object^ sender, System::EventArgs^ e)
{
testfunc1(label1);
}

Related

Print Date and Time in a form C++

could you fix me with my problem?
I have got a text field in my Form. And I would like to print Date and Time in a string,I mean, where cursor is.
I got this class for this:
#include <Windows.h>
//Russian letters are okay for this
private: System::Void времяИДатаToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
SYSTEMTIME sys_t;
GetSystemTime(&sys_t);
char szFormat[] = "MMMM";
char Buffer[16];
sys_t.wMonth = 1;
GetDateFormat(LOCALE_USER_DEFAULT, 0, &sys_t, szFormat, Buffer, 256);
}
};
I would like to use MessageBox::Show() , but it isn't for my problem?
Can you give some advice for this?
Thanks.
Standard warning: While it's certainly possible to write the main body of your application in C++/CLI, or even write the GUI in C++/CLI using WinForms, it is not recommended. C++/CLI is intended for interop scenarios: where C# or other .Net code needs to interface with unmanaged C++, C++/CLI can provide the translation between the two. For primary development, it is recommended to use C# with either WinForms or WPF if you want managed code, or C++ with MFC if you want unmanaged.
OK, that said: You've got the full .Net library available to you, why not use it?
void времяИДатаToolStripMenuItem_Click(Object^ sender, EventArgs^ e)
{
DateTime now = DateTime::Now;
String^ dateString = now.ToString("MMMM");
}
Assuming your text field is called textField, use:
textField->Text = new string(Buffer);
Also, your call to
GetDateFormat(LOCALE_USER_DEFAULT, 0, &sys_t, szFormat, Buffer, 256);
Is wrong. Pass 16, not 256 as the last argument (because you declared Buffer to have 16 chars).

Subscribe PropertyChanged events of window in C++/CLI

I just tried to subscribe to WPF property change events using C++/CLI. I didn't expect this to get difficult.
First I tried to subscribe to a specific property of some window (IsMouseDirectlyOver) and finally succeeded with following code:
void MyClass::DependencyPropertyChanged(Object^ sender, DependencyPropertyChangedEventArgs args)
{
Debug::WriteLine("DependencyPropertyChanged: "+sender->ToString()+", "+args.Property->Name);
}
window->IsMouseDirectlyOverChanged += gcnew DependencyPropertyChangedEventHandler(this, &MyClass::DependencyPropertyChanged);
Then I tried to subscribe to any property changes of an object (which is most important to me because my final code must be able to handle property changes by property names). I totally failed on this.
I tried various things but nothing worked. I could not find any C++/CLI examples but according to documentation and C# examples the following seemed to be the most sensible code to me:
window->PropertyChanged += gcnew PropertyChangedEventHandler(this, &MyClass::PropertyChanged);
void MyClass::PropertyChanged(Object^ sender, PropertyChangedEventArgs^ args)
{
...
}
But the compiler tells me by error C2039 that 'PropertyChangedEvent' is no element of 'System::Windows::Window'.
How can I achieve what I want?
Al mentioned in the comments, your code doesn't work, because there is no PropertyChanged event on Window, it's as simple as that.
What you can do instead is to override the OnPropertyChanged() method, which is present on a Window. In your override, you can do anything you want, including raising PropertyChanged (don't forget to create that event first).
I had a look on the snoop sources. I modified it and wrote a very, very basic example that works:
String^ ownerPropertyName = "IsActive";
DependencyObject^ propertyOwner = window;
DependencyPropertyDescriptor^ ownerPropertyDescriptor = DependencyPropertyDescriptor::FromName(ownerPropertyName, propertyOwner->GetType(), propertyOwner->GetType());
DependencyProperty^ ownerProperty = ownerPropertyDescriptor->DependencyProperty;
Type^ ownerPropertyType = ownerProperty->PropertyType;
DependencyProperty^ myProperty = DependencyProperty::Register(ownerPropertyName, ownerPropertyType, GetType(), gcnew PropertyMetadata(gcnew PropertyChangedCallback(&MyClass::BoundPropertyChangedCallback)));
Binding^ myPropertyToOwnerPropertyBinding = gcnew Binding(ownerPropertyName);
myPropertyToOwnerPropertyBinding->Mode = BindingMode::OneWay;
myPropertyToOwnerPropertyBinding->Source = propertyOwner;
BindingOperations::SetBinding(this, myProperty, myPropertyToOwnerPropertyBinding);
And:
static void BoundPropertyChangedCallback(DependencyObject^ me, DependencyPropertyChangedEventArgs args)
{
Debug::WriteLine("BoundPropertyChangedCallback: "+args.OldValue+", "+args.NewValue+", "+args.Property->Name);
}
Looks pretty complicated to me. I have no idea if that binding stuff is really necessary. In fact this can even subscribe to properties that do not have events (like IsMouseOver) and can operate on objects that do not implement INotifyPropertyChanged (like Window). And it does not need any switch/case for properties.
The class PropertyDescriptor (or the derived DependencyPropertyDescriptor) provides a mechanism to add a property change handler by their AddValueChanged method:
DependencyPropertyDescriptor^ propertyDescriptor = DependencyPropertyDescriptor::FromName(
"ActualWidth", component->GetType(), component->GetType());
propertyDescriptor->AddValueChanged(component, gcnew EventHandler(ActualWidthChanged));
...
static void ActualWidthChanged(Object^ component, EventArgs^ e)
{
...
}
Unfortunately the handler doesn't get passed the changed property, so i guess you would have to add different handlers for all properties you want to monitor.
EDIT: You might implement something like the code shown below that uses an anonymous delegate to pass the property name to an appropriate handler. Note however that this is C#, and to my understanding this can't be done in C++/CLI, since there it does not support managed lambdas. Mayby you could wrap a helper class like this in a separate assembly and use it from your C++/CLI code.
public delegate void PropertyChangedHandler(object component, string propertyName);
public static class DependencyPropertyDescriptorExt
{
public static void AddPropertyChangedHandler(
this object component, string propertyName, PropertyChangedHandler handler)
{
var propertyDescriptor = DependencyPropertyDescriptor.FromName(
propertyName, component.GetType(), component.GetType());
propertyDescriptor.AddValueChanged(component, (o, e) => handler(o, propertyName));
}
}
Now you could write and use such a PropertyChangedHandler like this:
this.AddPropertyChangedHandler("ActualHeight", PropertyChanged);
...
private void PropertyChanged(object component, string propertyName)
{
...
}

C# WinForms Adding ToolStripMenuItem dynamically. Why does this not work?

I have created an instance of a ToolStripMenuItem and wanted to add it as a submenu to two different menus on my form (to a contextmenu and a menu strip). I know how to get it to work but I am wondering why this doesn't work.
private static string[] parameters = { "itemOne", "itemTwo", "itemThree"};
private void MainForm_Load(object sender, EventArgs e)
{
foreach (string s in parameters)
{
ToolStripMenuItem addThis = new ToolStripMenuItem(s);
existingToolStripMenuItem.DropDownItems.Add(addThis);
existingMenuItem.DropDownItems.Add(addThis);
}
}
I noticed it works fine if I comment out one of the DropDownItems.Add() statements or if I create two separate instances. Why does it do this?
If you learn about the implementation of ToolStripItemCollection.Add, you will find that the second call existingMenuItem.DropDownItems.Add(addThis); removes addThis from existingToolStripMenuItem.DropDownItems.
So learning how to use decompilers such as ILSpy is critical for .NET developers,
http://wiki.sharpdevelop.net/ilspy.ashx
A possible workaround is to create two separate instances as you found out. If you intend to connect the two instances together, you can use ActionList,
http://www.lextm.com/2012/04/packaging-crads-actionlist-for-net-via-nuget/

Connecting 2 Textboxes values temporarily

I want to create 2 Textboxes (txt1, txt2) and when I write in txt1 then txt2 should reflect the same text what i typed in txt1. For ex. When we create a new Solution in Visual Studio Professional, what name we give to Project, same name appears for Solution. But if we edit solution name, link between the 2 textboxes breaks.
I do have some idea about it, to do it with textChange event or in fact many similar events, but not sure that they are the best methods.
I am using Winforms, C# 4.0, Visual Studio 2010 (if this info matters)
If my question is not clear, just make a comment I will try to elaborate.
Thanks.
With the given definition of the requirement, adding TextChanged EventHandlers is the way to go.
private void txt1_TextChanged(object sender, EventArgs e)
{
txt2.Text = txt1.Text;
}
private void txt2_TextChanged(object sender, EventArgs e)
{
txt1.Text = txt2.Text;
}
Consider adding an event handler for txt1_TextChanged and txt2_KeyPress.
txt1_TextChanged would assign txt2.Text: txt2.Text = "c:\" + txt1.Text;
txt2_KeyPress would unsubscribe txt1_TextChanged: txt1.TextChanged -= txt1_TextChanged;.
I solved it, so thought to post it here
txt1_TextChanged(obje....)
{
txt2.Text = txt1.Text;
}
txt2_TextChanged(objec...)
{
if(txt2.Focused)
{
txt1.TextChanged -= new EventHandler(txt1_TextChanged);
}
}
Hope it helps.

Communicating between forms

I have a main form from which I generate multiple subforms. I store these forms in a List<Subform^> ^ variable so that I can send messages to them from the main form. I load new forms like this (from memory, might not compile):
Subform ^sf = gcnew Subform(some, variables, here);
subforms->Add(sf);
subforms[subforms.Count-1]->Show();
My goal is to remove the subform from the list once it's closed. I've been considering moving to a dictionary for simpler form identification, like this:
++i; // Some sort of a form counter. to access them when closing.
Subform ^sf = gcnew Subform(some, variables, here);
subforms->Add(i, sf);
subforms[i]->Show();
How would I remove the ith form when closing? Perhaps something like this (in pseudocode)
sf->FormClosed = subforms->RemoveAt[i]; // Before I add it to the dictionary.
?
Try something like:
sf->FormClosed += gcnew FormClosedEventHandler(this, &RemoveSubform);
void RemoveSubform(System::Object^ sender, FormClosedEventArgs^ e)
{
subforms->Remove(sender);
}

Resources