MFC: CListCtrl LVS_SORTASCENDING not working? - winapi

I'm not sure why something so simple doesn't seem to work? I have a CFormView with a CListCtrl member variable that uses DDX. The Dialog already has the Ascending sort option set in the resource editor, but I even force it with ModifyStyle() it still doesn't sort, my added item always ends up at the end of the list. Groups are enabled, so not sure if that's causing an issue, but basically here's what I do below. Any ideas?
// have class with the CListCtrl and DDX
class CMyView : public CFormView
{
public:
CListCtrl m_MyList;
};
// init the CListCtrl
void CMyView::OnInitialUpdate()
{
__super::OnInitialUpdate();
m_MyList.ModifyStyle(0, LVS_SORTASCENDING);
if (m_MyList.GetHeaderCtrl()->GetItemCount()==0) {
// add our columns
m_MyList.InsertColumn(0, _T("The Column"), LVCFMT_LEFT, 500);
// enable groups
m_MyList.EnableGroupView(TRUE);
// add our groups
LVGROUP lvg={};
lvg.cbSize = sizeof(lvg);
lvg.mask = LVGF_HEADER|LVGF_GROUPID;
lvg.pszHeader = _T("Group Header 1");
lvg.iGroupId++;
if (m_MyList.InsertGroup(-1, &lvg)>=0) {
m_Group1=lvg.iGroupId;
}
lvg.pszHeader = _T("Group Header 2");
lvg.iGroupId++;
if (m_MyList.InsertGroup(-1, &lvg)>=0) {
m_Group2=lvg.iGroupId;
}
}
};
// adding item ends up at the end of the list - NOT sorted
void CMyView::AddListItem(const TCHAR* str)
{
LVITEM lvi={};
lvi.mask|=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM|LVIF_GROUPID;
lvi.pszText=const_cast<TCHAR*>(str);
lvi.lParam=0;
lvi.iImage=ICONidxSomething;
lvi.iGroupId=m_Group1;
lvi.iItem=m_MyList.GetItemCount();
m_MyList.InsertItem(&lvi);
}

According to the explanation in the official documentation.
For the LVS_SORTASCENDING and LVS_SORTDESCENDING styles, item indexes
are sorted based on item text in ascending or descending order,
respectively. Because the LVS_LIST and LVS_REPORT views display items
in the same order as their indexes, the results of sorting are
immediately visible to the user. The LVS_ICON and LVS_SMALLICON views
do not use item indexes to determine the position of icons. With those
views, the results of sorting are not visible to the user.
We need to set the correct List View properties.
LONG lstyle;
//Get the style of the current listctrl
lstyle = GetWindowLong(m_MyList.m_hWnd, GWL_STYLE);
//clear display
lstyle &= ~LVS_TYPEMASK;
//set style
lstyle |= LVS_REPORT;
SetWindowLong(m_MyList.m_hWnd, GWL_STYLE, lstyle);
OR change in VS Properties window.

Related

How to create a Data Table from forge viewer

I want to create a table that represents the data from a model that i have loaded in the forge viewer.
i want the table to be in a docking panel, and show the propertys for elements in the model ( level, name, comment,Area, type name--> for each element [if it has the property])
i have tried to use the API reference, and create a DataTable, but i did not find examples of how to actually impliment it.
where and when do i need to set the datatable? ( after or before creating the docking pannel?)
what is the content of the arrays that i should pass in the constructor? ( according to the documentation : array of arrays for the rows, and array for the columns. is the row array, is simply an array that contains the columns arrays?)
this is my current code for the extension that shows the amount to instances in the model for each property that i want to adjust:
'''
class testTest extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super(viewer, options);
this._group = null;
this._button = null;
}
load() {
console.log('testTest has been loaded');
return true;
}
unload() {
// Clean our UI elements if we added any
if (this._group) {
this._group.removeControl(this._button);
if (this._group.getNumberOfControls() === 0) {
this.viewer.toolbar.removeControl(this._group);
}
}
console.log('testTest has been unloaded');
return true;
}
// The Viewer contains all elements on the model, including categories (e.g. families or part definition),
//so we need to enumerate the leaf nodes, meaning actual instances on the model.
getAllLeafComponents(callback) {
this.viewer.getObjectTree(function (tree) {
let leaves = [];// an empty 'leaf' list that we want to fill wiith the objects that has no mo children
//dbId== object id
// for each child that we enumerate from a root, call a code , and finally a true /false flag parameter that run the function recursivly for all the children of a child.
tree.enumNodeChildren(tree.getRootId(), function (dbId) {
if (tree.getChildCount(dbId) === 0) {
leaves.push(dbId);// if the object has no children--> add it to the list.
}
}, true);// the last argument we past ("true") will make sure that the function in the seccond argument ("function (dbId)(...)" ")will run recursively not only for the children of the roots,
//but for all the children and childrtn's children.
callback(leaves);//return the leaves
});
}
onToolbarCreated() {
// Create a new toolbar group if it doesn't exist
this._group = this.viewer.toolbar.getControl('allMyAwesomeExtensionsToolbar');//if there is no controller named "allMyAwesomeExtensionsToolbar" create one
if (!this._group) {
this._group = new Autodesk.Viewing.UI.ControlGroup('allMyAwesomeExtensionsToolbar');
this.viewer.toolbar.addControl(this._group);// add the control to tool bar
}
// Add a new button to the toolbar group
this._button = new Autodesk.Viewing.UI.Button('testTest');
this._button.onClick = (ev) => {
// Check if the panel is created or not
if (this._panel == null) {//check if there is an instance of our pannel. if not- create one
this._panel = new ModelSummaryPanel(this.viewer, this.viewer.container, 'modelSummaryPanel', 'Model Summary');
}
// Show/hide docking panel
this._panel.setVisible(!this._panel.isVisible());//cal a method from the parent to show/ hide the panel -->use this to toggle from visible to invisible
this._panel.set
// If panel is NOT visible, exit the function
if (!this._panel.isVisible())
return;
// First, the viewer contains all elements on the model, including
// categories (e.g. families or part definition), so we need to enumerate
// the leaf nodes, meaning actual instances of the model. The following
// getAllLeafComponents function is defined at the bottom
this.getAllLeafComponents((dbIds) => {// now we have the list of the Id's of all the leaves
// Now for leaf components, let's get some properties and count occurrences of each value
debugger;
const filteredProps = ['Level','Name','Comments','Area','Type Name'];
// Get only the properties we need for the leaf dbIds
this.viewer.model.getBulkProperties(dbIds,filteredProps , (items) => {
// Iterate through the elements we found
items.forEach((item) => {
// and iterate through each property
item.properties.forEach(function (prop) {
// Use the filteredProps to store the count as a subarray
if (filteredProps[prop.displayName] === undefined)
filteredProps[prop.displayName] = {};
// Start counting: if first time finding it, set as 1, else +1
if (filteredProps[prop.displayName][prop.displayValue] === undefined)
filteredProps[prop.displayName][prop.displayValue] = 1;
else
filteredProps[prop.displayName][prop.displayValue] += 1;
});
});
// Now ready to show!
// The PropertyPanel has the .addProperty that receives the name, value
// and category, that simple! So just iterate through the list and add them
filteredProps.forEach((prop) => {
if (filteredProps[prop] === undefined) return;
Object.keys(filteredProps[prop]).forEach((val) => {
this._panel.addProperty(val, filteredProps[prop][val], prop);
this.dt = new DataTabe(this._panel);
this.dt.setData()
});
});
});
});
};
this._button.setToolTip('Or Levis extenssion');
this._button.addClass('testTest');
this._group.addControl(this._button);
}
}
Autodesk.Viewing.theExtensionManager.registerExtension('testTest', testTest);'''
'''
We have a tutorial with step-by-step on how to do it.
Please, refer to Dashboard tutorial, specifically in Data Grid section.
In this case, the tutorial uses an external library (Tabulator) to show the data.

ComboBox.Item.add(USB1) switch language----- globalization - VS 2019 WPF

I added ComboBox from MainWindow_OnContentRendered, when I start the program, and how ComboBox.Item will find the resource file to change different language?> .How can I put WPF ComboBox content globalization.Thank you.
hello.
A.
1.
public void MyComboBox()
{
ComboBox.Item.add(USB1)
ComboBox.Item.add(USB2)
ComboBox.Item.add(USB3)
}
2.
MainWindow_OnContentRendered
{
MyComboBox();
}
B.
//ResourceHelper.cs
public static void LoadResource(string ) {
var = (from d in _Resourcelist where d.ToString().Equals() select d).FirstOrDefault();
App.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri(langType, UriKind.RelativeOrAbsolute) });
Thread.CurrentThread.CurrentCulture = cultureInfo;
hread.CurrentThread.CurrentUICulture = cultureInfo;}
The question sounds quite simple but is quite difficult to answer. I've just started a new WPF application from scratch, so I thought about the issue of switching to a different language at runtime in general. Of course you have to set CurrentCulture and CurrentUICulture like you do in your example. But what about the controls and their textual content?
My solution is a recursive method that I call with MainWindow.Content as parameter, and then it iterates deeper and deeper through the hierarchy of controls:
private static void ReloadAllText(object root)
{
if (root is TextBlock textBlock1)
{
UpdateBinding(textBlock1, TextBlock.TextProperty);
}
else if (root is ContentControl contentControl)
{
if (contentControl.Content is string)
{
UpdateBinding(contentControl, ContentControl.ContentProperty);
}
else
{
ReloadAllText(contentControl.Content);
}
}
else if (root is Panel panel)
{
foreach (var child in panel.Children)
{
ReloadAllText(child);
}
}
else if (root is ItemsControl itemsControl)
{
for (int cnt = 0, cntMax = VisualTreeHelper.GetChildrenCount(itemsControl); cnt < cntMax; cnt++)
{
if (VisualTreeHelper.GetChild(itemsControl, cnt) is TextBlock textBlock2)
{
ReloadAllText(textBlock2);
}
}
foreach (var item in itemsControl.Items)
{
ReloadAllText(item);
}
}
else if (root is Decorator decorator)
{
ReloadAllText(decorator.Child);
}
else if (root is IRaiseLanguageChanged customItem)
{
customItem.RaiseLanguageChanged();
}
}
The method consists of several branches:
For TextBlock (which is also used by default as the text display element inside other, more complicated controls), the Text property is set to the new value. In my case, I just update the binding. In your case, the new text may have a different source, I don't know your architechture.
For ContentControl (which is any control that has a Content property), it depends: If the content is just a string, I can set it to the new value right away. If it's more complex, then I have to recurse deeper.
For Panel (which is the base class for StackPanel, DockPanel, Grid etc.), I just recurse for each child element.
For ItemsControl (so also for your ComboBox!), I recurse for each item. I added the VisualTree part only because I have a control template for an empty list box consisting of only a TextBox saying "no items". If you bind ItemsSource to an enum type, you must renew the ItemsSourceProperty binding.
For Decorator (e.g. Border), I recurse for its single child.
For custom/self-made controls, I have defined a custom interface IRaiseLanguageChanged, so they must implement a RaiseLanguageChanged() method and handle the language switch themselves. After all, a control itself knows best what to do when the language changes.
This reflects only the set of controls I'm currently using. If you have additional control types, then you have to add respective branches. Please post them here, if you have any good ideas!

My CellTable does not sort

I red a lot about sorting a CellTable. I also went trough the ColumnSorting with AsyncDataProvider. But my CellTable does not sort.
Here is my code:
public class EventTable extends CellTable<Event> {
public EventTable() {
EventsDataProvider dataProvider = new EventsDataProvider(this);
dataProvider.addDataDisplay(this);
SimplePager.Resources pagerResources = GWT.create(SimplePager.Resources.class);
SimplePager pager = new SimplePager(TextLocation.CENTER, pagerResources, false, 5, true);
pager.setDisplay(this);
[...]
TextColumn<Event> nameCol = new TextColumn<Event>() {
#Override
public String getValue(Event event) {
return event.getName();
}
};
nameCol.setSortable(true);
AsyncHandler columnSortHandler = new AsyncHandler(this);
addColumnSortHandler(columnSortHandler);
addColumn(nameCol, "Name");
getColumnSortList().push(endCol);
}
}
public class EventsDataProvider extends AsyncDataProvider<Event> {
private final EventTable eventTable;
public EventsDataProvider(EventTable eventTable) {
this.eventTable = eventTable;
}
#Override
protected void onRangeChanged(HasData<Event> display) {
int start = display.getVisibleRange().getStart();
int length = display.getVisibleRange().getLength();
// check false values
if (start < 0 || length < 0) return;
// check Cache before making a rpc
if (pageCached(start, length)) return;
// get Events async
getEvents(start, length);
}
}
I do now know, if all the methods are need here. If so, I will add them. But in short:
pageCached calls a method in my PageCache Class which holds a map and a list. Before making a rpc call, the cache is checked if the events where already taken and then displayed.
getEvents just makes an rpc call via asynccallback which updates the rowdata via updateRowData() on success.
My Table is displayed fast with currently around 500 entries (could be more, depends on the customer). No missing data and the paging works fine.
I just cannot get the sorting work. As far as I know, AsyncHandler will fire a setVisibleRangeAndClearData() and then an onRangeChanged(). onRangeChanged is never fired. As for the setVisibleRangeAndClearData() I do not know. But the sortindicator (arrow next to the columnname) does change on every click.
I do not want to let the server sort the list. I have my own Comparators. It is enough, if the current visible page of the table is sorted. I do now want to sort the whole list.
Edit:
I changed following code in the EventTable constructor:
public EventTable() {
[...]
addColumnSortHandler(new ColumnSortEvent.AsyncHandler(this) {
public void onColumnSort(ColumnSortEvent event) {
super.onColumnSort(event);
MyTextColumn<Event> myTextColumn;
if (event.getColumn() instanceof MyTextColumn) {
// Compiler Warning here: Safetytype unchecked cast
myTextColumn = (MyTextColumn<Event>) event.getColumn();
MyLogger.log(this.getClass().getName(), "asc " + event.isSortAscending() + " " + myTextColumn.getName(), Level.INFO);
}
List<Event> list = dataProvider.getCurrentEventList();
if (list == null) return;
if (event.isSortAscending()) Collections.sort(list, EventsComparator.getComparator(EventsComparator.NAME_SORT));
else Collections.sort(list, EventsComparator.descending(EventsComparator.getComparator(EventsComparator.NAME_SORT)));
}
});
addColumn(nameCol, "Name");
getColumnSortList().push(endCol);
}
I had to write my own TextColumn to determine the Name of the column. Otherwise how should I know, which column was clicked? The page gets sorted now but I have to click twice on the column. After then, the sorting is done with every click but in the wrong order.
This solution does need polishing and it seems kinda hacky to me. Any better ideas?
The tutorial, that you linked to, states:
This sorting code is here so the example works. In practice, you would
sort on the server.
Async provider is used to display data that is too big to be loaded in a single call. When a user clicks on any column to sort it, there is simply not enough objects on the client side to display "first 20 evens by name" or whatever sorting was applied. You have to go back to your server and request these first 20 events sorted by name in ascending order. And when a user reverses sorting, you have to go to the server again to get first 20 events sorted by name in a descending order, etc.
If you can load all data in a single call, then you can use regular DataProvider, and all sorting can happen on the client side.
EDIT:
The problem in the posted code was in the constructor of EventsDataProvider. Now it calls onRangeChanged, and the app can load a new sorted list of events from the server.

How to change value of a property 'SelectedValue' of 'ListPicker' control programmatically

I have two 'ListPicker' controls. How could I make it so that after user changes 'SelectedValue' property of one of these controls a corresponding property of a second control would also change?
Can someone please provide us a example?
Thanks a lot
you can set select item like this code.when you fill list at the time you can set selected item property.
private void FillAssetType(int assetTypeID)
{
List<TbAssetType> lst = Asset.GetAssetType();
TbAssetType tb = new TbAssetType { AssetTypeName = "Select Asset Type" };
lst.Insert(0, tb);
lstpickAssetType.ItemsSource = lst;
lstpickAssetType.SelectedItem = lst.Where(p => p.AssetTypeID == assetTypeID).FirstOrDefault();
}

Cascading to a auto completing text box

I have a web page where the user will enter their address. They will select their country and region in cascading drop down lists. I would like to provide an auto completing textbox for their city, but I want to be context sensitive to the country and region selections. I would have just used another cascading drop down list, however the number of cities exceeds the maximum number of list items.
Any suggestions or cool code spinets out there that may help me out?
I just found the following blog post that looks at least close to what you want.
They manage it using the following javascript functions:
function initCascadingAutoComplete() {
var moviesAutoComplete = $find('autoCompleteBehavior1');
var actorsAutoComplete = $find('autoCompleteBehavior2');
actorsAutoComplete.set_contextKey(moviesAutoComplete.get_element().value);
moviesAutoComplete.add_itemSelected(cascade);
// setup initial state of second flyout
if (moviesAutoComplete.get_element().value) {
actorsAutoComplete.get_element().disabled = false;
} else {
actorsAutoComplete.get_element().disabled = true;
actorsAutoComplete.get_element().value = "";
}
}
function cascade(sender, ev) {
var actorsAutoComplete = $find('autoCompleteBehavior2');
actorsAutoComplete.set_contextKey(ev.get_text());
actorsAutoComplete.get_element().value = '';
if (actorsAutoComplete.get_element().disabled) {
actorsAutoComplete.get_element().disabled = false;
}
}
Sys.Application.add_load(initCascadingAutoComplete);
Calling the cascade function on the add_itemSelected method of the parent control for the cascading behaviour.
They cascade the contents of one auto complete extender into another, rather than taking a cascading drop down list, but hopefully you can reuse some of the ideas.

Resources