I'm trying to write a C++/CLI forms application that creates a lot of buttons at runtime: I have a vector of strings and for each string a button is being created:
std::vector<std::string> strings;
/*
string being initialized with values from file
*/
for ( std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it ) {
Button ^ button = gcnew Button;
/*
button being customized depending on the string
*/
buttonPannel->Controls->Add(button);
}
Now what I want to do is add an event handler for each button in a way that the string used to customize the button would be passed to handling method.
In c# I would have written something like
button->Click += new EventHandler((sender, args) => button_Click(s, e, *it));
How do I achieve this in C++/CLI?
You could do the exact equivalent of your C# code, but I'd rather make use of an existing property on the Button class to hold the extra data you need.
In this case, the Tag property seems appropriate: its purpose is to hold any extra data you need that is closely associated with the control, so this seems on-point for your string that drives the program logic. (You may need to make it a managed String^ object, rather than a std::string, but that's an easy conversion.)
void Form1::CreateButtons()
{
for (std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it)
{
Button ^ button = gcnew Button;
button->Tag = marshal_as<String^>(*it);
button->Click += gcnew EventHandler(this, &Form1::button_Click);
buttonPanel->Controls->Add(button);
}
}
void Form1::button_Click(Object^ sender, EventArgs^ e)
{
Control^ senderControl = dynamic_cast<Control^>(sender);
String^ heroName = nullptr;
if(senderControl != nullptr)
heroName = dynamic_cast<String^>(senderControl->Tag);
if(heroName == nullptr)
{
// Something went wrong. Bail out.
return;
}
// ...
}
If you really do want to do the equivalent of your C# code: Your C# lambda is doing variable capture on the it variable. We can do variable capture in C++/CLI, it's just a lot more manual.
(Note: Your C# example is capturing the iterator, not the string, not sure if that's what was intended. I wrote this to capture the string object instead.)
ref class EventHandlerStringCapture
{
public:
EventHandlerStringCapture(std::string str,
Action<Object^, EventArgs^, std::string>^ handler)
{
this->str = str;
this->handler = handler;
}
void eventHandler(Object^ sender, EventArgs^ e)
{
this->handler(sender, e, this->str);
}
private:
std::string str;
Func<Object^, EventArgs^, std::string>^ handler;
}
void Form1::CreateButtons()
{
for (std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it)
{
Button ^ button = gcnew Button;
// The variable to capture.
std::string str = *it;
// The actual event handler: a method in the current class.
Action<Object^, EventArgs^, std::string>^ actualHandler =
gcnew Action<Object^, EventArgs^, std::string>(this, &Form1::button_Click);
// Pass both the variable to capture and the
// actual event handler to a helper object.
EventHandlerStringCapture^ ehsc =
gcnew EventHandlerStringCapture(str, actualHandler);
// Grab the two-parameter event handler from the helper object,
// and make that the click handler.
button->Click +=
gcnew EventHandler(ehsc, &EventHandlerStringCapture::eventHandler);
buttonPanel->Controls->Add(button);
}
}
void Form1::button_Click(Object^ sender, EventArgs^ e, std::string heroName)
{
// ...
}
(Note: I'm not at a compiler, so there may be syntax errors.)
Obviously, using an existing property on the button object is simpler, but that's the C++/CLI equivalent to what the C# compiler does behind the scenes.
Related
So when writing UI in GTK it's generally preferrable to handle reading of files, etc. in an Async Method. things such as listboxes, are generally bound to a ListModel, the items in the ListBox updated in accordance with the items_changed signal.
So if I have some class, that implements ListModel, and has an add function, and some FileReader that holds a reference to said ListModel, and call add from an async function, how do i make that in essence triggering the items_changed and having GTK update accordingly?
I've tried list.items_changed.connect(message("Items changed!")); but it never triggers.
I saw this: How can one update GTK+ UI in Vala from a long operation without blocking the UI
but in this example, it's just the button label that is changed, no signal is actually triggered.
EDIT: (Codesample added at the request of #Michael Gratton
//Disclaimer: everything here is still very much a work in progress, and will, as soon as I'm confident that what I have is not total crap, be released under some GPL or other open license.
//Note: for the sake of readability, I adopted the C# naming convention for interfaces, namely, putting a capital 'I' in front of them, a decision i do not feel quite as confident in as I did earlier.
//Note: the calls to message(..) was put in here to help debugging
public class AsyncFileContext : Object{
private int64 offset;
private bool start_read;
private bool read_to_end;
private Factories.IVCardFactory factory;
private File file;
private FileMonitor monitor;
private Gee.Set<IVCard> vcard_buffer;
private IObservableSet<IVCard> _vCards;
public IObservableSet<IVCard> vCards {
owned get{
return this._vCards;
}
}
construct{
//We want to start fileops at the beginning of the file
this.offset = (int64)0;
this.start_read = true;
this.read_to_end = false;
this.vcard_buffer = new Gee.HashSet<IVCard>();
this.factory = new Factories.GenericVCardFactory();
}
public void add_vcard(IVCard card){
//TODO: implement
}
public AsyncFileContext(IObservableSet<IVCard> vcards, string path){
this._vCards = vcards;
this._vCards = IObservableSet.wrap_set<IVCard>(new Gee.HashSet<IVCard>());
this.file = File.new_for_path(path);
this.monitor = file.monitor_file(FileMonitorFlags.NONE, null);
message("1");
//TODO: add connect
this.monitor.changed.connect((file, otherfile, event) => {
if(event != FileMonitorEvent.DELETED){
bool changes_done = event == FileMonitorEvent.CHANGES_DONE_HINT;
Idle.add(() => {
read_file_async.begin(changes_done);
return false;
});
}
});
message("2");
//We don't know that changes are done yet
//TODO: Consider carefully how you want this to work when it is NOT called from an event
Idle.add(() => {
read_file_async.begin(false);
return false;
});
}
//Changes done should only be true if the FileMonitorEvent that triggers the call was CHANGES_DONE_HINT
private async void read_file_async(bool changes_done) throws IOError{
if(!this.start_read){
return;
}
this.start_read = false;
var dis = new DataInputStream(yield file.read_async());
message("3");
//If we've been reading this file, and there's then a change, we assume we need to continue where we let off
//TODO: assert that the offset isn't at the very end of the file, if so reset to 0 so we can reread the file
if(offset > 0){
dis.seek(offset, SeekType.SET);
}
string line;
int vcards_added = 0;
while((line = yield dis.read_line_async()) != null){
message("position: %s".printf(dis.tell().to_string()));
this.offset = dis.tell();
message("4");
message(line);
//if the line is empty, we want to jump to next line, and ignore the input here entirely
if(line.chomp().chug() == ""){
continue;
}
this.factory.add_line(line);
if(factory.vcard_ready){
message("creating...");
this.vcard_buffer.add(factory.create());
vcards_added++;
//If we've read-in and created an entire vcard, it's time to yield
message("Yielding...");
Idle.add(() => {
_vCards.add_all(vcard_buffer);
vcard_buffer.remove_all(_vCards);
return false;
});
Idle.add(read_file_async.callback);
yield;
message("Resuming");
}
}
//IF we expect there will be no more writing, or if we expect that we read ALL the vcards, and did not add any, it's time to go back and read through the whole thing again.
if(changes_done){ //|| vcards_added == 0){
this.offset = 0;
}
this.start_read = true;
}
}
//The main idea in this class is to just bind the IObservableCollection's item_added, item_removed and cleared signals to the items_changed of the ListModel. IObservableCollection is a class I have implemented that merely wraps Gee.Collection, it is unittested, and works as intended
public class VCardListModel : ListModel, Object{
private Gee.List<IVCard> vcard_list;
private IObservableCollection<IVCard> vcard_collection;
public VCardListModel(IObservableCollection<IVCard> vcard_collection){
this.vcard_collection = vcard_collection;
this.vcard_list = new Gee.ArrayList<IVCard>.wrap(vcard_collection.to_array());
this.vcard_collection.item_added.connect((vcard) => {
vcard_list.add(vcard);
int pos = vcard_list.index_of(vcard);
items_changed(pos, 0, 1);
});
this.vcard_collection.item_removed.connect((vcard) => {
int pos = vcard_list.index_of(vcard);
vcard_list.remove(vcard);
items_changed(pos, 1, 0);
});
this.vcard_collection.cleared.connect(() => {
items_changed(0, vcard_list.size, 0);
vcard_list.clear();
});
}
public Object? get_item(uint position){
if((vcard_list.size - 1) < position){
return null;
}
return this.vcard_list.get((int)position);
}
public Type get_item_type(){
return Type.from_name("VikingvCardIVCard");
}
public uint get_n_items(){
return (uint)this.vcard_list.size;
}
public Object? get_object(uint position){
return this.get_item((int)position);
}
}
//The IObservableCollection parsed to this classes constructor, is the one from the AsyncFileContext
public class ContactList : Gtk.ListBox{
private ListModel list_model;
public ContactList(IObservableCollection<IVCard> ivcards){
this.list_model = new VCardListModel(ivcards);
bind_model(this.list_model, create_row_func);
list_model.items_changed.connect(() => {
message("Items Changed!");
base.show_all();
});
}
private Gtk.Widget create_row_func(Object item){
return new ContactRow((IVCard)item);
}
}
Heres the way i 'solved' it.
I'm not particularly proud of this solution, but there are a couple of awful things about the Gtk ListBox, one of them being (and this might really be more of a ListModel issue) if the ListBox is bound to a ListModel, the ListBox will NOT be sortable by using the sort method, and to me at least, that is a dealbreaker. I've solved it by making a class which is basically a List wrapper, which has an 'added' signal and a 'remove' signal. Upon adding an element to the list, the added signal is then wired, so it will create a new Row object and add it to the list box. That way, data is control in a manner Similar to ListModel binding. I can not make it work without calling the ShowAll method though.
private IObservableCollection<IVCard> _ivcards;
public IObservableCollection<IVCard> ivcards {
get{
return _ivcards;
}
set{
this._ivcards = value;
foreach(var card in this._ivcards){
base.prepend(new ContactRow(card));
}
this._ivcards.item_added.connect((item) => {
base.add(new ContactRow(item));
base.show_all();
});
base.show_all();
}
}
Even though this is by no means the best code I've come up with, it works very well.
I'm trying to add a customized UI page to Sparx EA. It provides adding ActiveX controls via scripting. Using JScript, I've done this, but since ActiveX has to be registered on each client, I'd rather use Microsoft Forms, already installed on all clients.
I've successfully built the UI, appearance wise, by adding a "Forms.Form.1" ActiveX object, and adding text boxes, labels & buttons to the controls property of the created form.
These objects support events, but I can't figure out how to assign an event handler.
Here is the JScript code I used to get the screen layout:
function _addControl(parentControl, controlProgId, controlName, left, top, width, height){
var newControl = parentControl.controls.add(controlProgId, controlName,1);
newControl.Name=controlName;
newControl._SetLeft(left);
newControl._SetTop(top);
newControl._SetWidth(width);
newControl._SetHeight(height);
return newControl;
}
function main(){
//Create main form
var form = Repository.AddTab("ScriptedForm", "Forms.Form.1");
if (null != form){
//Add control
var textBox1 = _addControl(form, "Forms.TextBox.1","TextBox1", 18,21,94,93);
var textBox2 = _addControl(form, "Forms.TextBox.1","TextBox2", 120, 21, 91, 93);
var btnTest = _addControl(form, "Forms.CommandButton.1", "btnTest", 60, 140, 90, 30);
btnTest.Caption = "Test";
//Here's where I assign the click event, but it's unhappy.
btnTest.add_Click(this.TextBox1_Click);
}
}
function TextBox1_Click(Object){
Session.Prompt("Click", promptOK);
}
The add_Click event expects a parameter of type CommandButtonEvents_ClickEventHandler.
There's nothing I can create that could be submitted as the parameter. I tried creating a JScript class duplicating the interface, but no joy.
I think you are running into several problems here at once.
(1) Process Lifetime
As far as I understand your question and its context, you are somehow manually executing a JScript script. Doing this EA will internally start SScripter.exe. You see this in the Debug window:
The process is effectively terminated after the script finishes (and thus also terminating any event handlers you might have registered in your UserControl or Form object).
(2) Passing a JScript object instance as a .NET delegate
If you could somehow extend the lifetime of the script environment, and if you could pass something to your event you will realise that any object in your JScript code will be passed as a System.__ComObject to the .NET runtime inside EA. Therefore you cannot just register an event handler.
However when you evaluate the object from .NET you will find out it is not an IDispatch interface:
MemberNames:
ToString,
GetLifetimeService,
InitializeLifetimeService,
CreateObjRef,
Equals,
GetHashCode,
GetType
TargetInvocationException#mscorlib: 'COM target does not implement IDispatch.'
I did a small test with the code below:
function MyClass(name)
{
this.name = name;
}
MyClass.prototype.Invoke = function(value)
{
Session.Output("name " + value);
return true;
}
function main()
{
var myClass = new MyClass("Hotzenplotz");
myClass.Invoke("some Value");
var ctrl = new ActiveXObject("IMASE.TestUserControl2");
ctrl.Repository = Repository;
ctrl.JavaScriptObject = myClass;
}
[ProgId(Global.ADDIN_NAME + Global.DOT + "TestUserControl2")]
[Guid("87156dd9-e947-44bf-92a9-e9554a5b1844")]
[ComVisible(true)]
public partial class TestUserControl2 : ActivexControl
{
public static string TabName { get; } = Global.ADDIN_NAME;
private static readonly Lazy<string> _controlId = new Lazy<string>(() =>
{
var attribute = typeof(TestUserControl).GetCustomAttribute<ProgIdAttribute>();
return attribute.Value;
});
private Timer timer;
public static string ControlId = _controlId.Value;
public Repository Repository { get; set; }
public object JavaScriptObject { get; set; }
public TestUserControl2()
{
timer = new Timer();
timer.Elapsed += TimerEvent;
timer.Interval = 5000;
timer.Enabled = true;
timer.Start();
}
~TestUserControl2()
{
Logger.Default.TraceInformation("I'm gonna die ... " + this.GetHashCode());
}
private void OnDispose(object sender, EventArgs e)
{
timer.Dispose();
}
private void TimerEvent(object source, ElapsedEventArgs e)
{
Logger.Default.TraceInformation("I'm still alive ... " + this.GetHashCode());
if(null == JavaScriptObject) return;
try
{
var memberNames = JavaScriptObject.GetType().GetMembers(BindingFlags.Instance|BindingFlags.FlattenHierarchy|BindingFlags.Public).Select(p => p.Name);
Logger.Default.TraceInformation("memberNames: " + string.Join(", ", memberNames));
var result = JavaScriptObject.GetType().InvokeMember("Invoke", BindingFlags.InvokeMethod, null, JavaScriptObject, new object[] {"arbitraryString"});
Logger.Default.TraceInformation("result: " + result);
}
catch (Exception ex)
{
Logger.Default.TraceException(ex);
}
}
}
(3) Another approach
Create a UserControl inside your addin (using WinForm or Forms) and use ClearScript as a ScriptEngine.
Pass Session and Repository from a EA script to your control (or do it otherwise such as a menu to wprk around the lifetime problem) and have either your forms code load a script from the repository (or any other source). Then react on your event handlers to execute your JScript code as needed. I create a simple example that shows how to call a control from an EA JScript and call another JScript from inside your forms code that in turn will log to the Debug session or regular scripting output window:
function main()
{
var ctrl = new ActiveXObject("IMASE.TestUserControl2");
ctrl.Repository = Repository;
ctrl.Session = Session;
Session.Prompt("wait", promptOK);
}
main();
Inside your form code you invoke your JScript with Repository and other objects like this:
public Repository Repository { get; set; }
public object Session { get; set; }
using (var engine = new JScriptEngine())
{
engine.AddHostObject("Repository", this.Repository);
engine.AddHostObject("Session", this.Session);
engine.Execute("Session.Output('Repository.ConnectionString: ' + Repository.ConnectionString);");
}
Here is an output of the above scripting interactions:
Side note: I personally do not see the need for using forms as we can dynamically register ActiveX controls at AddIn startup. For code on doing this you can have a look at the following gist:
https://gist.github.com/dfch/6a27bb1b9320c93456cee6d5b2b9d551
In addition, if you are using ClearScript as the script host, you can directly connect to your (UI) events from your script code as described in question #16 of the ClearScript FAQtorial.
in my vsto addin i have some simple code on a timer
private void MainTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!dialogopen & Application.Documents.Count > 0)
{
var doc = Application.ActiveDocument;
Word.InlineShapes shps;
Word.Paragraphs pars;
try
{
pars = doc.Paragraphs;
}
catch (Exception)
{
return;
}
var pars2 = pars.Cast<Word.Paragraph>().ToList();
foreach (var obj in pars2)
{
if (obj.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText )//PROBLEM HERE
{
};
}
}
}
as soon as it reaches the line that checks the outlinelevel, even if i dont do a thing, the selection in the activedocument gets lost
of course the user cant use a plugin which keeps on canceling his selection...
googling didnt give me a thing
thanks
EDIT1
I tried making a static function for checking the styles, but it did not help. Here's the code
static public class Helpers
{
static public Word.Paragraph checkPars(List<Word.Paragraph> pars)
{
return pars.FirstOrDefault();//(x) => x.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText);
}
}
As you can see, I had to remove the actual check, since it was causing the cursor to blink and lose selection
please advise
We use the Application.ScreenUpdating = true; and this keep the selection for all properties in Paragraph except for the Range property.
Then, we tried to access the range via Reflection and this was the solution.
Range rng = (Range)typeof(Paragraph).GetProperty("Range").GetValue(comObj);
We tried to eliminate querying ActiveDocument thinking that that might have had side-effects that were causing the problem but that was not the case.
Then, we confirmed that the selection was not "lost" and screen-updating is the only problem, so we tried restoring the UI with Application.ScreenRefresh and while it did work, it causes the screen to flicker every time the timer fires and this is not good enough.
Finally, knowing that it's only a UI problem, I tried simply switching off Application.ScreenUpdating...
in ThisAddin
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Timer timer = new Timer(2000);
timer.Elapsed += (s, t) =>
{
var scrnUpdating = Application.ScreenUpdating;
Application.ScreenUpdating = false;
MainTimer.onElapsed(Application, t);
if (scrnUpdating)
Application.ScreenUpdating = true;
};
timer.Start();
}
In another class library (note that it's static, I still think this is the best way)...
public static class MainTimer
{
public static void onElapsed (Word.Application state, System.Timers.ElapsedEventArgs e)
{
if (state.Documents.Count > 0)
{
var doc = state.ActiveDocument;
Word.InlineShapes shps;
Word.Paragraphs pars;
try
{
pars = doc.Paragraphs;
}
catch (Exception)
{
return;
}
var pars2 = pars.Cast<Word.Paragraph>()
.Where(p => p.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText)
.Select(p => p) // do stuff with the selected parragraphs...
.ToList();
}
}
}
And this works for me.
The selection remains and is displayed in the UI without flicker.
I also eliminated some premature enumeration from your code: you don't meed the .ToList() before the foreach.
I'm making a Windows Forms Application in VS2012 C++.
Situation just for example, real project is more complicated:
I have a Form that contains TextBox, Button and Timer.
Button just triggers the timer. Timer just calls function that increments some variable.
I need to display the function's variable that is incremented, in TextBox.
In Form1.h I add code:
public: void Timer_Function(); //function activated by timer Tick
void Set_Text(String ^str); //function to set TextBox Text
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
if (timer1->Enabled == false) timer1->Enabled = true;
else timer1->Enabled = false;
}
private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e)
{
Timer_Function();
}
In My_app.cpp code like this:
#include "stdafx.h"
#include "Form1.h"
#include "resource.h"
using namespace test_staticfunc;
[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew Form1());
return 0;
}
void Form1::Timer_Function()
{
Timer_Func();
}
void Form1::Set_Text(String ^str)
{
textBox1->Text = str;
}
void Timer_Func()
{
static int I=0;
I++;
Form1::Set_Text(I.ToString());
}
Function Timer_Func() is specified in "resource.h" like this:
void Timer_Func();
I.e. I'm trying to display the current state of inner variable I of Timer_Func() by passing it to a Form1 public method Set_Text().
So. The error here is that Set_Text() is not a static method.
I tried to make it static, but got an error "ะก2227: The operand to the left of "->Text" is not a pointer to a class, structure, or union." How to get it right? In that case a static method is trying to implement a non-static method, right?
Or another way: to make an instance of Form1 - instead of
Application::Run(gcnew Form1());
insert code
Form1 ^My_form = gcnew Form1();
Application::Run(My_form);
And use Set_Text as non-static method for class instance My_form.
But My_form is available only in main()! I couldn't make My_form anywhere else. Is there way to make it global or something?
May be there are other ways to solve this problem?
Help, please! I've already searched several forums for answer but didn't find the answer. More precisely non of them suited.
P.S. Sorry for my bad english! ^_^
please, I've spent all day trying to figure this out but can't. I have a class (artist1) created from a dialog box with some edit boxes. I want to get the data typed in the edit boxes and save them to variables i made public in the class. But don't know why it doesn't work. PS am new to mfc programming. Thanks
here is my artist class
void artist1::OnBnClickedButton1()
{
//artist1 AA=*art1;
CEdit* pEdit1 = (CEdit*)GetDlgItem(IDC_EDIT1);
pEdit1->GetWindowText(Name1);
nn=new CString;
*nn=Name1;
CEdit* pEdit2 = (CEdit*)GetDlgItem(IDC_EDIT2);
pEdit2->GetWindowText(Age1);
n2=new CString;
*n2=Age1;
CEdit* pEdit3 = (CEdit*)GetDlgItem(IDC_EDIT3);
pEdit3->GetWindowText(Nationality1);
n3=new CString;
*n3=Nationality1;
CEdit* pEdit4 = (CEdit*)GetDlgItem(IDC_EDIT4);
pEdit4->GetWindowText(Group1);
n4=new CString;
*n4=Group1;
CEdit* pEdit5 = (CEdit*)GetDlgItem(IDC_EDIT5);
pEdit5->GetWindowText(num_of_albums1);
n5=new CString;
*n5=num_of_albums1;
SH(Name1,Age1,Nationality1,Group1,num_of_albums1);
art1=this;
// memcpy(art1,this,sizeof(this));
//Name_box.SetWindowText(g);
//AfxMessageBox( Age );
//AfxMessageBox( Nationality );
// TODO: Add your control notification handler code here
}
/*bool artist1::SH()
{
if(NoShow==false)return true;
else return false;
}*/
void artist1::OnBnClickedButton2()
{
//Cooplab1View vm;
NoShow=false;
nvalidateRect(NULL,NULL);
EndDialog(IDD_FORMVIEW);
// TODO: Add your control notification handler code here
}
and here is the class artist header
class artist1 : public CDialogEx
{
//DECLARE_DYNAMIC(artist1)
public:
artist1(CWnd* pParent = NULL); // standard constructor
virtual ~artist1();
bool NoShow;
bool *address;
CString Albums[5];
void OnInsertArtist(artist1 &at);
// Dialog Data
enum { IDD = IDD_FORMVIEW };
private:
CString Nm;
CString Ag;
CString Nation;
CString group;
CString No_of_A;
CString *nnn;
public:
// artist1* GetTreeObj();
//virtual CString ShowDetails(CDC* pDC);
void SH(CString a,CString b,CString c,CString d,CString e)
{
Name=a;
Age=b;
Nationality=c;
Group=d;
num_of_albums=e;
}
protected:
CString Name,Age,Nationality,Group,num_of_albums;
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
CString Name1,Age1,Nationality1,Group1,num_of_albums1;
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedButton1();
virtual CString ShowDetails(CDC* pDC,artist1 & at1);
afx_msg void OnBnClickedButton2();
};
and i call the class from the CView class cpp file
void Cooplab1View::OnDraw(CDC* pDC)
{
artist1 art;
artist1 A1;
Cooplab1Doc* pDoc = GetDocument();
//ASSERT_VALID(pDoc);
//if (!pDoc)
// return;
if (noShow)
{
art.OnInsertArtist(art);
//art.OnBnClickedButton1();
//art=&obj;
// art.GetTreeObj();
art.ShowDetails(pDC,art);
}
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
}
void Cooplab1View::OnInsertArtist1()
{
noShow=true;
InvalidateRect(NULL,NULL);
//UpdateWindow();
}
You should create variables (right click->Add variables in vs2010) matching your "edit boxes" and check their values.
Don't do "GetDlgItem" and not GetWindowText.
You should addUpdateData(TRUE) at the first line of OnBnClickedButton1
Good Luck !
Your class is quite messy, but if you just want to extract user entry text from the dialog items, it is actually quite simple.
void artist1::OnBnClickedButton1()
{
// Since variables Name,Age,Nationality,Group,num_of_albums are all CString items
// declare as member variables in the class,
// you can just retrieve user entry from the dialog directly into them
// without calling the "SH(Name1,Age1,Nationality1,Group1,num_of_albums1);"
GetDlgItemText(IDC_EDIT1, Name);
GetDlgItemText(IDC_EDIT2, Age);
GetDlgItemText(IDC_EDIT3, Nationality);
GetDlgItemText(IDC_EDIT4, Group);
GetDlgItemText(IDC_EDIT5, num_of_albums);
}
If you are entering a value to the edit control, you should call updatedata( FALSE ). If you want to store the value into a variable then call updatedata(TRUE). That's it.
I finally solved the problem. I declared some global variables, assigned the values inputted in the edit boxes to them in OnBnClickedButton1() using GetDlgItemText then in OnBnClickedButton2() i assigned the global variables to the variables in my class ie Name, Age etc.