MFC Picture Control not displaying bitmap when control variable is changed - visual-c++

I have a MFC Dialog based application. I have placed a Picture Control (of type Bitmap) to display an initial/default resource bitmap. That displays just fine when the app starts.
When a user selects an item in a CListbox, I want to change the bitmap resource displayed. A CStatic control variable m_Bitmap was created and I change it based on the users' listbox selection. Then I update the controls.
Upon execution, the original bitmap simply disappears and the control fails to display the new bitmap. I have used the same technique with static text control variables and CStrings and it works fine.
Why are my bitmaps failing to change? Tried calling the picture controls' RedrawWindow() function with a CWnd pointer which does nothing either.
This should be an easy thing to do in MFC...
//Code Snippet
//
//Picture Control (IDC_BitmapCntl), control variable is m_Bitmap
// DDX_Control(pDX, IDC_BitmapCntl, m_Bitmap);
//
//Code from CList Control, OnLbnSelchange() function, CListbox variable is m_Selection
//
switch (m_Selection) { //Select a coresponding bitmap to display
case (0):
m_Bitmap.SetBitmap((HBITMAP)IDB_Bitmap1);
break;
case (1):
m_Bitmap.SetBitmap((HBITMAP)IDB_Bitmap2);
break;
//additional cases ommited for brevity
default:
break;
}
UpdateData(FALSE); //this should update the control but does not display new bitmap
//Failed attempt to then redraw control
CWnd* pDlg;
pDlg = GetDlgItem(IDC_BitmapCntl);
pDlg->RedrawWindow(); //cannot access OnPaint() via a pointer
//end snippet
No errors on compilation. Initial bitmap image displays OK but disappears when user selects an item in listbox. New bitmap is not displayed.

Assuming that IDB_Bitmap1 and IDB_Bitmap2 are (integral) resource identifiers for the bitmaps you want (defined in an .rc or .rc2 file), then you can't just simply cast them with (HBITMAP)IDB_Bitamp1 (HBITMAP is actually a pointer, and it will then point to who-knows-what => the dreaded undefined behaviour).
You have to use the LoadBitmap() function (or something similar) to get the actual bitmap from the application's resources. The simplest way is using a (local) CBitmap object:
//...
CBitmap bitmap;
switch (m_Selection) { //Select a coresponding bitmap to display
case 0: // Don't really need brackets around case 'values'
bitmap.LoadBitmap(IDB_Bitmap1); // "LoadBitmap" will have "W" appended for Unicode builds
break; // or "A" appended for non-Unicode ('ASCII') builds.
case 1:
bitmap.LoadBitmap(IDB_Bitmap2); // Probably best practice to use the 'native' names?
break;
// Additional cases ...
default: // Strictly speaking, unnecessary, but I like to put this catch-all in ...
break; // ... and good for you, for also having it!
}
m_Bitmap.SetBitmap(bitmap.operator HBITMAP());
// Strict way to do it, but you can omit the ".operator HBITMAP()" in MOST cases.
Hope this helps!
PS: You really ought to do something (in the default case, maybe) to put a 'valid' bitmap in CBitmap for un-handled cases.

Related

Is it possible to recolor a lottie animation programmatically?

If I have a lottie animation in the form of a json file, is there a way to recolor it in code or even within the json itself?
(To be clear, I hope there's a way to do it without involving After Effects. For instance if I decide to change my app's primary color, the whole app will change except the animation unless there's a way to do that.)
I figured it out. For this example, let's say I want to recolor a specific layer to Color.RED.
You'll need your LottieAnimationView, a KeyPath, and a LottieValueCallback
private LottieAnimationView lottieAnimationVIew;
private KeyPath mKeyPath;
private LottieValueCallback<Integer> mCallback;
Then in your onCreate (or onViewCreated for a fragment) you'll get the animation with findViewById, as well as "addLottieOnCompositionLoadedListener" to the lottieAnimationView, in which you will setup the "mKeyPath" and "mCallback":
lottieAnimationVIew = findViewById(R.id.animationView);
lottieAnimationView.addLottieOnCompositionLoadedListener(new LottieOnCompositionLoadedListener() {
#Override
public void onCompositionLoaded(LottieComposition composition) {
mKeyPath = getKeyPath(); // This is your own method for getting the KeyPath you desire. More on that below.
mCallback = new LottieValueCallback<>();
mCallback.setValue(Color.RED);
checkBox.addValueCallback(mKeyPath, LottieProperty.COLOR, mCallback);
}
});
The argument "LottieProperty.COLOR" specifies which property I am changing.
There's probably a better way to do this, but here's my "getKeyPath" method for finding the specific thing I want to change. It will log every KeyPath so you can see which one you want. Then it returns it once you've supplied the correct index. I saw that the one I want is the 5th in the list, hence the hard-coded index of 4.
private KeyPath getKeyPath() {
List<KeyPath> keyPaths = lottieAnimationView.resolveKeyPath(new KeyPath("Fill", "Ellipse 1", "Fill 1"));
for (int i = 0; i < keyPaths.size(); i++) {
Log.i("KeyPath", keyPaths.get(i).toString());
}
if (keyPaths.size() == 5) {
return keyPaths.get(4);
}
else {
return null;
}
}
Note that the "Fill", "Ellipse 1", "Fill 1" are strings I supplied to narrow the list down to just the ones that have those keys, because I know that the layer I want will be among those. There's likely a better way to do this as well.
There is another thread on this topic with the same approach but a bit simplified:
How to add a color overlay to an animation in Lottie?
Here's directly an example (Kotlin):
yourLottieAnimation.addValueCallback(
KeyPath("whatever_keypath", "**"),
LottieProperty.COLOR_FILTER
) {
PorterDuffColorFilter(
Color.CYAN,
PorterDuff.Mode.SRC_ATOP
)
}
You can find the names of the keypaths also in the Lottie editor.

UWP - Proper way of passing parameters between pages

Suppose I want to pass one object (reference) through several pages. I can navigate and pass parameters via Frame.Navigate(typeof(FirstPage), object). But how to pass the reference back on back press properly?
protected override void OnNavigatedTo(NavigationEventArgs e) {
if (e.Parameter is SomeClass) {
this.someObject = (SomeClass)e.Parameter;
}
else {
this.someObject = new SomeClass();
}
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
SystemNavigationManager.GetForCurrentView().BackRequested += OnHardwareButtonsBackPressed;
base.OnNavigatedTo(e);
}
private void OnHardwareButtonsBackPressed(object sender, BackRequestedEventArgs e) {
// This is the missing line!
Frame.Navigate(typeof(FirstPage), this.someObject);
}
But when I press back button it goes back to the FirstPage OnNavigatedTo with no parameter, and then back to the SecondPage OnHardwareButtonsBackPressed and then back to FirstPage OnNavigatedTo with filled parameter.
Could you please advice me some better approach?
In your back handler, don't navigate forwards again, just call GoBack -- and it's typically easier if you handle that at a global level rather than at a page level.
You can store your application state (the things you want to persist across page navigations) in global / static objects, or you could directly modify the object that was passed from the initial navigation (if the calling page still has a reference, it will be able to see the changes).
I would consider doing a search for "MVVM Windows Apps" and looking at some of the results to learn about a common way of building XAML apps.

CDocTemplate and m_templateList

I am upgrading some software from 16 bit to 32 bit in VC++ using MFC, and I understand that in recent versions of MFC I can no longer access m_templateList in CDocTemplate, I must use GetFirstDocTemplatePosition and GetNextDocTemplate instead. That is no problem as far as enumerating templates is concerned (a dialog being opened only in the case where there is more than one template). My question is what approach is best to get round the fact that a reference to the template list is currently being passed to the dialog on creation, and a selected template is being returned? Here is the code:
void CMtApp::OnFileNew()
{
CString s;
if (m_templateList.IsEmpty())
{
TRACE0("Error : no document templates registered with CWinApp\n");
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
return;
}
CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();
if (m_templateList.GetCount() > 1)
{
// more than one document template to choose from
// bring up dialog prompting user
COpenTypeDlg dlg(&m_templateList);
if (dlg.DoModal() != IDOK)
return; // none - cancel operation
pTemplate = dlg.m_pSelectedTemplate;
pTemplate->GetDocString(s, CDocTemplate::docName);
}
ASSERT(pTemplate != NULL);
ASSERT(pTemplate->IsKindOf(RUNTIME_CLASS(CDocTemplate)));
m_bNew = TRUE;
pTemplate->OpenDocumentFile(NULL);
}
You can pass the CWinApp to the dialog's ctor and the dialog can GetFirstDocTemplatePosition and GetNextDocTemplate itself. But you don't really need to pass the CWinApp because the dialog can use AfxGetApp to get it itself.
If you insist on passing a template list then build your own list based on what GetFirstDocTemplatePosition and GetNextDocTemplate return.

Create Property Sheet in Frame Window

I am using an MDI application. I want to create a property sheet inside Frame Window area as shown by arrow in image below:
I have seen examples where we can use ShowWindow() function after creating property sheet but it creates property sheet which is not embedded in frame window.
Can we create propertysheet on frame window only like other controls as static box etc?
If you need to embed a resizable property sheet to the view, please take a look at BCGSoft size(http://www.bcgsoft.com) - the latest BCGControlBar from version shows how to do it:
http://www.bcgsoft.com/images/resizableform220.jpg
If you simply need a tabbed MDI windows, just create a Visual Studio-like application in MFC AppWizard (VS 2008 or later).
Hope, this helps.
Rob
Adding CMultiDocTemplate instances solved my problem. Here is code snippet. This is part of ProjectName.cpp file:
BOOL CEmuDiagnosticsClientApp::InitInstance()
{
// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinAppEx::InitInstance();
// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
//Added new code
{
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_STRING_LOGGINGWINDOW,
RUNTIME_CLASS(CEmuDiagnosticsClientDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CLoggingWindow));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
}
//End: Added new code
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need
// Change the registry key under which our settings are stored
// TODO: You should modify this string to be something appropriate
// such as the name of your company or organization
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(4); // Load standard INI file options (including MRU)
InitContextMenuManager();
InitKeyboardManager();
InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_STRING_SIGNALWINDOW,
RUNTIME_CLASS(CEmuDiagnosticsClientDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CSignalWindow)); //Changed Code
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
{
delete pMainFrame;
return FALSE;
}
m_pMainWnd = pMainFrame;
// call DragAcceptFiles only if there's a suffix
// In an MDI app, this should occur immediately after setting m_pMainWnd
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line. Will return FALSE if
// app was launched with /RegServer, /Register, /Unregserver or /Unregister.
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The main window has been initialized, so show and update it
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
In //Added New code section, created a new CMultiDocTemplate instance. CLoggingWindow is the class which I wanted to display in frame window.
Another class CSignalWindow I also wanted to display which is modified in //changed code area.
Things to remember:
-Dialog which you want to display must be derived from CFormView, not CDialog.
-Changed dialog property: Border -> None, Style -> Child and all other properties to false.

MonoTouch.Dialog row animations

Just wondering if it is possible to have no animations in section when adding/removing elements in the section. Or even when adding/removing the section itself.
On ViewWillAppear I check a global boolean to see if the app has been exited/reopened. This will then cause it to reload new data. (Did have RefreshRequested set up, but the client wanted it this way). So on ViewWillAppear we call a function called UpdateCells which does the following.
// Using Bottom animation as its the closest to none (none has no fade effects
// but makes solid cells move upwards in the TableView and then suddenly disappear)
Root.Remove(mySection1, UITableViewRowAnimation.Bottom);
Root.Remove(mySection2, UITableViewRowAnimation.Bottom);
mySection1.Clear();
mySection2.Clear();
// Load stuff from SQLite DB and populate sections. Adding looks like:
// if (isThisWeek)
// {
// section1.Insert(0, UITableViewRowAnimation.None, newElement);
// }
// else
// {
// section2.Insert(0. UITableViewRowAnimation.None, newElement);
// }
// Now finally add sections back into Root if there exists elements in them.
if (sectionThisWeek.Count > 0)
{
Root.Insert(1, UITableViewRowAnimation.None, sectionThisWeek);
}
if (sectionCommingUp.Count > 0)
{
Root.Insert(1, UITableViewRowAnimation.None, sectionCommingUp);
}
I was hoping for some sort of, pause everything in the DialogViewController, work on it, then do a ReloadComplete() with no animations.. somehow...
Any ideas what I'm doing wrong, or a better way to do what I am trying to do.
If you are reloading all the data at once, you can just assign the entire new value to the Root property on the DialogViewController and no animations would be shown.

Resources