Show all lables on x-Axis Live charts Wpf - wpf

Trying to plot some data on line chart. I am fetching data from Datbase and getting x-axis and y-axis values. Adding values in label of X-axis like below.
<wpf:CartesianChart Name="MonthlySalesLineChart" LegendLocation="Bottom" Margin="10" FontWeight="ExtraLight" Foreground="WhiteSmoke">
<wpf:CartesianChart.DataTooltip>
<wpf:DefaultTooltip BulletSize="20" Background="Black" Opacity="0.8" Foreground="White"/>
</wpf:CartesianChart.DataTooltip>
</wpf:CartesianChart>
Code Behind :
private string[] _labelXAxis;
_labelXAxis = new string[_saleInvoiceList.Count];
for (int i = 0; i < _saleInvoiceList.Count; i++)
{
_labelXAxis[i] = _saleInvoiceList[i].Date.ToString("M");
}
and then assigning to chart like this.
MonthlySalesLineChart.AxisX.Add(new Axis
{
Labels = _labelXAxis,
// ShowLabels = true,
Foreground = Brushes.White,
Separator = new LiveCharts.Wpf.Separator
{
Step = 1,
},
Title = "Date"
// LabelFormatter = value => value.ToString("M")
// MinValue = 1,
});
Now problem is if i turn on Separator then on x-axis both values are shown 0, 1,2,3,4,5... which i am not adding with my _labelXAxis values which are string type date.
But if i don't use separator then i don't see all my string dates on x-Axis instead only few. why ?
When separator is not implemented
When Separator is implemented

Related

Issue with x-axis ticks in dc.js

I am using d3 v5.9.2, dc v3.0.12 to render a line chart but the ticks in x-axis seems to be having some issue. The data supplied has to be plotted with date along x Axis and value along y-axis since it's a timeline graph.
const line = lineChart(divRef);
const crossfilterDimension = dx.dimension(dimension);
const firstRecord = crossfilterDimension.bottom(1)[0];
const lastRecord = crossfilterDimension.top(1)[0];
line
.dimension(crossfilterDimension)
.x(
scaleTime().domain([
firstRecord[Object.keys(firstRecord)[0]],
lastRecord[Object.keys(lastRecord)[0]]
])
)
.xUnits(timeYear)
.xAxisLabel(xAxis.label)
.yAxisLabel(yAxis.label)
.renderDataPoints({ radius: 2, fillOpacity: 0.8, strokeOpacity: 0.0 })
.group(
count
? crossfilterDimension.group().reduceCount()
: crossfilterDimension.group().reduceSum(group)
);
line
.yAxis()
.ticks(yAxis.ticks)
.tickFormat(yAxis.tickFormat);
return line;

React component with SVG does not evaluate expression

Imagine I have a component like this it encloses an SVG:
class Image extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "Initial message"
};
}
render() {
return ( < svg xmlns = "http://www.w3.org/2000/svg"
width = "300"
height = "450" >
< rect x = "0"
y = "0"
width = "300"
height = "450"
fill = "#d0d0d0" / >
< text id = "textInHere"
x = "50%"
y = "50%"
fill = "#7d7d7d" > {this.state.message} < /text> < /svg > )
}
}
I am simply evaluating expression this.state.message as inner text.
To my surprise it generates the following DOM structure:
This is strange as i have not added any span!
The problem is illustrated in a plunk here
Appreciate, if any one could explain why I see multiple spans, and/or how to fix this problem.
You have a problem with whitespaces in your tags. When you remove them everything looks fine.
UPDATE: apparently, this is caused by spaces surrounding {this.state.message}. They are converted to span which results in breaking your syntax as you have observed (because span is not allowed inside svg tag).

How do I set the ComboBox width to fit the largest item

I would like that my ComboBox has to adapt its width to the longest String Item of my list.
Code Example:
ComboBox {
model: [ "Banana", "Apple", "ThisIsTheLongestWordThatIHave,"Coconut" ]
}
Any idea of how to do it?
There is no built-in mechanism for this in Quick-Controls-2 combobox (at the time of writing, Qt 5.9), so you have to do it yourself. Something like this...
main.qml
MyComboBox {
id: comboBox1
sizeToContents: false
model: [ "Banana", "Apple", "ThisIsTheLongestWordThatIHave", "Coconut" ]
}
MyComboBox {
id: comboBox2
anchors.top: comboBox1.bottom
sizeToContents: true
model: [ "Banana", "Apple", "ThisIsTheLongestWordThatIHave", "Coconut" ]
}
MyComboBox.qml
ComboBox {
id: control
property bool sizeToContents
property int modelWidth
width: (sizeToContents) ? modelWidth + 2*leftPadding + 2*rightPadding : implicitWidth
delegate: ItemDelegate {
width: control.width
text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
font.family: control.font.family
font.pointSize: control.font.pointSize
highlighted: control.highlightedIndex === index
hoverEnabled: control.hoverEnabled
}
TextMetrics {
id: textMetrics
}
onModelChanged: {
textMetrics.font = control.font
for(var i = 0; i < model.length; i++){
textMetrics.text = model[i]
modelWidth = Math.max(textMetrics.width, modelWidth)
}
}
}
Note that if you change the model type from a QML List to a different type, such as C++ QStringList, QList<QObject*> or QAbstractListModel, then you migth need to modify this line textMetrics.text = model[i] to retrieve the text from the model items in a slightly different way.
As of Qt 6, this is now possible by setting the ComboBox's implicitContentWidthPolicy to either ComboBox.WidestText , which will update whenever the model changes, or ComboBox.WidestTextWhenCompleted, which will check just once, when the ComboBox is loaded. (Keep in mind that the latter might not work as expected if the model isn't already available at the instant the ComboBox is loaded.)
#Mark Ch - MyComboBox doesn't work with Controls 2; the width of the indicator is not taken into account so it is too narrow if the indicator has any width.
It worked for me by replacing the assignment for width: with the following:
width: sizeToContents
? (modelWidth + leftPadding + contentItem.leftPadding
+ rightPadding + contentItem.rightPadding)
: implicitWidth
Here's a different approach which is less dependent on internals, works with any kind of model, and with alternate ComboBox styles e.g. "material":
The idea is to just set currentItem to each possible value and let the ComboBox internals do their thing; then observe the resulting widths. ComboBox.contentItem is a TextField, and TextField.contentWidth has what we want. We don't have to know how to iterate the model or emulate what a delegate might do to change formatting. The desired ComboBox width is the max of those contentWidths, plus padding and indicator width.
The calculation can not be directly bound to width because a binding loop would occur. Instead, width is calculated and set statically when the onCompleted signal occurs.
Note: The following code doesn't yet handle dynamically updated models. I may update this post later...
USAGE:
import QtQuick 2.9
import QtQuick.Controls 2.2
import "ComboBoxHacks.js" as CBH
...
ComboBox {
id: myCB
Component.onCompleted: width = CBH.calcComboBoxImplicitWidth(myCB)
...
}
And here is the javascript code:
/* ComboBoxHacks.js */
function calcComboBoxImplicitWidth(cb) {
var widest = 0
if (cb.count===0) return cb.width
var originalCI = cb.currentIndex
if (originalCI < 0) return cb.width // currentIndex → deleted item
do {
widest = Math.max(widest, cb.contentItem.contentWidth)
cb.currentIndex = (cb.currentIndex + 1) % cb.count
} while(cb.currentIndex !== originalCI)
return widest + cb.contentItem.leftPadding + cb.contentItem.rightPadding
+ cb.indicator.width
}
You just need to update the minimumWidth when the model changes.
import QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.12
ComboBox {
id: root
onModelChanged: {
var _maxWidth = 0
for(var i = 0; i < model.length; i++){
// TextMetrics does not work with Material Style
_maxWidth = Math.max((model[i].length+1)*Qt.application.font.pixelSize, _maxWidth)
}
Layout.minimumWidth = _maxWidth + implicitIndicatorWidth + leftPadding + rightPadding
}
}

ng-grid - Keep overall width the same when altering column widths

I have an ng-grid with 6 columns in it, and as a default set up each column is 100px, so the grid itself is 600px. The columns are resizable but I want to keep the overall grid width the same, to ensure that there are no horizontal scroll bars. So, for example, if I change the second columns width to 150px I would want the overall width to stay at 600px (so maybe an adjacent cell will change size to 50px) - this way I don't get a scroll bar.
Does anybody know if there is a plugin that can do this/help me accomplish this?
I've included a plunker: http://plnkr.co/edit/4LRHQPg7w2eDMBafvy6b?p=preview
In this example, I would want to keep the table width at 600px, so if I expand "Field 2" you will see "Field 4" go off the edge of the viewable area for the grid and a horizontal scroll bar appear. The behaviour I want is for a different column (probably the adjacent column - "Field 3") to shrink in size automatically, so that the grid stays at 600px and the horizontal scroll bar doesn't appear.
After a lot of time searching the web for an answer, I started with Paul Witherspoon idea of watching the isColumnResizing property and after reading about ng-grid plugins I came up with this plugin solution:
Plunker: http://plnkr.co/edit/Aoyt73oYydIB3JmnYi9O?p=preview
Plugin code:
function anchorLastColumn () {
var self = this;
self.grid = null;
self.scope = null;
self.services = null;
self.init = function (scope, grid, services) {
self.grid = grid;
self.scope = scope;
self.services = services;
self.scope.$watch('isColumnResizing', function (newValue, oldValue) {
if (newValue === false && oldValue === true) { //on stop resizing
var gridWidth = self.grid.rootDim.outerWidth;
var viewportH = self.scope.viewportDimHeight();
var maxHeight = self.grid.maxCanvasHt;
if(maxHeight > viewportH) { // remove vertical scrollbar width
gridWidth -= self.services.DomUtilityService.ScrollW;
}
var cols = self.scope.columns;
var col = null, i = cols.length;
while(col == null && i-- > 0) {
if(cols[i].visible) {
col = cols[i]; // last column VISIBLE
}
}
var sum = 0;
for(var i = 0; i < cols.length - 1; i++) {
if(cols[i].visible) {
sum += cols[i].width;
}
}
if(sum + col.minWidth <= gridWidth) {
col.width = gridWidth - sum; // the last gets the remaining
}
}
});
}
}
and in the controller
$scope.gridOptions = {
data: 'myData',
enableColumnResize: true,
plugins: [new anchorLastColumn()],
columnDefs: $scope.columnDefs
};
It is not a perfect solution but works for me and I hope that it will help others.
I found a way to do this using a watch on the isColumnResizing property:
$scope.$watch('gridOptions.$gridScope.isColumnResizing', function (newValue, oldValue) {
if (newValue === false && oldValue === true) { //on stop resizing
$scope.ColResizeHandler($scope.gridOptions.$gridScope.columns);
}
}, true);
then I was able to resize the columns in the resize handler I created:
$scope.ColResizeHandler = function (columns) {
var origWidth;
var col1 = undefined;
var col2 = undefined;
var widthcol2;
var found = false;
var widthDiff = 0;
angular.forEach(columns, function (value) {
if (col2 == undefined && value.visible) {
if (found) {
origWidth += value.width;
col2 = value;
colSizeLimits(col2, widthDiff);
found = false;
}
if (value.origWidth != undefined && value.origWidth != value.width && col2 == undefined) {
found = true;
col1 = value;
widthDiff = value.width - value.origWidth;
origWidth = value.origWidth;
}
}
});
if (col2 == undefined) {
//this was the last visible column - don't allow resizing
col1.width = origWidth;
}
else {
//ensure limits haven't been blown to cope with reizing
if (col1.width + col2.width != origWidth) {
var diff = (col1.width + col2.width) - origWidth;
colSizeLimits(col1, diff);
}
}
col1.origWidth = col1.width;
col2.origWidth = col2.width;
}
There are 2 issues with this.
1 - if you resize and drag the column sizer outside of the grid (i.e. all the way over and out of the ng-grid viewable area) the isColumnResizing watch doesn't execute when you stop dragging and release the resizer. (I think this may be a bug in ng-grid because it does actually resize the column to where you have dragged the resizer, even if it is outside the grids viewable area, it just doesn't fire the watch code).
2 - if you avoid this issue and just drag within the viewable grid area then the columns will resize but only after you finish dragging the resizer, so the ui looks a little funny (i.e. if I expand a column then the adjacent column will not shrink until I click off the resizer and stop dragging).
I'll be working on these issues and will post any updates/fixes I find.

ExtJs Chart, How to get boundary dates from selection mask on 'Time' type axis

I have ExtJs 4 Area chart with Time serie. I'd like user to be able to horizontally select part of chart and then obtain higher density data from server adequately. Problem is I can't get boundary dates from selection. I've got:
var chart = Ext.create('Ext.chart.Chart', {
store: store,
enableMask: true,
mask: 'horizontal',
listeners: {
select: {
fn: function(me, selection) {
console.log(arguments); // selection = Object { height: 218, width: 117, x: 665, y: 123 }
}
},
...
But select listener provides only pixel data. Is there some way to get boundary axis data (e.g. { from: 2013-08-01, to: 2013-08-20 } or some way to unproject pixels to values? I'm desperade I would say it's such a basic thing but can't find solution anywhere. Thanks in advance.
Well.. it probably doesn't exists a method for this. After digging into source code I've utilized lines from chart.setZoom() method to create function for manual unprojecting of mask selection to X axis data:
var unprojectXAxis = function(chart, selection) {
zoomArea = {
x : selection.x - chart.el.getX(),
width : selection.width
};
xScale = chart.chartBBox.width,
zoomer = {
x : zoomArea.x / xScale,
width : zoomArea.width / xScale
}
ends = chart.axes.items[0].calcEnds();
from = (ends.to - ends.from) * zoomer.x + ends.from;
to = (ends.to - ends.from) * zoomer.width + from;
return { from: new Date(from), to: new Date(to) };
}

Resources