I'm creating a dynamic VSD from a hierarchical set of data that represents a flowchart. I don't want/need to fuddle with absolute positioning of these elements - the automatic layout options will work just fine.
The problem is I can't figure out how to perform this command via code. In the UI (Visio 2010), the commands are on the ribbon here: Design (tab) -> Layout (group) -> Re-Layout (SplitButton).
Any of these will do. Looking through the Visio SDK documentation and Googling for a couple days have turned up nothing of very much use.
Any ideas? (using C#, but VB/VBA would do)
The Page.Layout() method itself is not enough.
In the WBSTreeView.sln sample project (VB.Net) I found how to accomplish this, but couldn't post my answer until 8 hours later :-x
The other layout types are possible by looking through the enums used below.
Compact -> DownRight just ended up being better for most of the flows we're creating.
Translated to C#:
// auto-layout, Compact Tree -> Down then Right
var layoutCell = this._page.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOPlaceStyle);
layoutCell.set_Result(
VisUnitCodes.visPageUnits,
(short)VisCellVals.visPLOPlaceCompactDownRight);
layoutCell = this._page.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLORouteStyle);
layoutCell.set_Result(
VisUnitCodes.visPageUnits,
(short)VisCellVals.visLORouteFlowchartNS);
//// to change page orientation
//layoutCell = this._page.PageSheet.get_CellsSRC(
// (short)VisSectionIndices.visSectionObject,
// (short)VisRowIndices.visRowPrintProperties,
// (short)VisCellIndices.visPrintPropertiesPageOrientation);
//layoutCell.set_Result(
// VisUnitCodes.visPageUnits,
// (short)VisCellVals.visPPOLandscape);
// curved connector lines
layoutCell = this._page.PageSheet.get_CellsSRC(
(short)VisSectionIndices.visSectionObject,
(short)VisRowIndices.visRowPageLayout,
(short)VisCellIndices.visPLOLineRouteExt);
layoutCell.set_Result(
VisUnitCodes.visPageUnits,
(short)VisCellVals.visLORouteExtNURBS);
// perform the layout
this._page.Layout();
// optionally resize the page to fit the space taken by its shapes
this._page.ResizeToFitContents();
//
Changing Connector Line Colors
If you're unfamiliar with how formulas for colors work, this might also be very frustrating. By default you can give an int as a string to get pre-defined colors, but this isn't very helpful because there isn't an easy way to figure out what each of those colors are. (There is a Page.Colors collection, but you have to inspect each of their RGB values and figure out the color from them.)
Instead, you can use your own RGB values for the formula.
private void SetConnectorLineColor(Shape connector, string colorFormula)
{
var cell = connector.get_Cells("LineColor");
cell.Formula = colorFormula;
}
internal static class AnswerColorFormula
{
public static string Green = "RGB(0,200,0)";
public static string Orange = "RGB(255,100,0)";
public static string Yellow = "RGB(255,200,0)";
public static string Red = "RGB(255,5,5)";
}
Call the Layout method on the Page object. If there are shapes selected on this page then this method will only operate on the current selection. You may want to call DeselectAll on the ActiveWindow first.
Related
I'm writing a small program in which multiple shapes will be visible in JavaFX. I'm trying to create a button through which it is possible to change all the shape's color to the chosen one by the user.
Right now, I'm only able to do that by changing each shape individually in my lambda expression. It's okay for now since there's only three shapes, but it will be inconvenient further one. Can anyone think of a way to group all the shapes together and access the "setFill" method to change them all at once?
Here's the code:
// Calling Semi-Circle method
Arc semicircle1 = shapes1.getsemicircle();
// create Colors button and set is as invisible until a shape value is passed by user
Button buttonColors = new Button();
buttonColors.setText("Choose a color for the Shape");
buttonColors.setVisible(true);
buttonColors.setOnAction( e ->
{
if (textField1.getText().equalsIgnoreCase("Grey"))
{
label1.setText("Grey");
semicircle1.setFill(Color.GREY);
}
});
You may create some property and bind all shapes to it
final ObjectProperty<Paint> fillProperty = new SimpleObjectProperty(Color.GREY);
...
semicirle1.fillProperty().bind(fillProperty);
pentagon1.fillProperty().bind(fillProperty);
rectangle1.fillProperty().bind(fillProperty);
...
fillProperty.set(Color.RED);
I'm trying to create a chart in winforms that databinds to a list in memory, and gets updated dynamically as the list changes. Here is my code:
open System
open System.Linq
open System.Collections
open System.Collections.Generic
open System.Drawing
open System.Windows.Forms
open System.Windows.Forms.DataVisualization
open System.Windows.Forms.DataVisualization.Charting
let link = new LinkedList<double>()
let rnd = new System.Random()
for i in 1 .. 10 do link.AddFirst(rnd.NextDouble()) |> ignore
let series = new Series()
let chart = new System.Windows.Forms.DataVisualization.Charting.Chart(Dock = DockStyle.Fill, Palette = ChartColorPalette.Pastel)
series.Points.DataBindY(link)
let form = new Form(Visible = true, Width = 700, Height = 500)
form.Controls.Add(chart)
let formloop = async {
while not chart.IsDisposed do
link.AddFirst((new System.Random()).NextDouble()) |> ignore
link.RemoveLast()
}
do
Async.StartImmediate(formloop)
Application.Run(form)
Console.WriteLine("Done")
Console.ReadLine() |> ignore
The async seems to work, but the chart never shows anything. It just shows a blank window. What am I doing wrong?
LinkedList<T> has no way to signal that it's been updated, so Chart has no way of knowing when to redraw itself.
In order for databinding to update the view, the source list must implement IBindingList and raise appropriate event when the contents change.
Separately, I must point out that it's dangerous to directly access UI properties/methods from non-UI threads (such as chart.IsDisposed in your code). In WinForms, this limitation is rarely actually enforced, so sometimes this might seem to work fine, only to crash later on a customer's machine with no way to attach a debugger.
You need to add the series to the SeriesCollection of the chart.
chart.Series.Add series
You need to construct a chart area and add it to the ChartAreaCollection of the chart.
let area = new ChartArea()
chart.ChartAreas.Add area
You need to make sure that the data binding method is called after the chart and form are set up.
...
form.Controls.Add chart
series.Points.DataBindY link
And now there's no way to communicate changes of your bound collection to the DataPointCollection of the series, as mentioned in Fyodor Soikin's answer. I'm not quite sure that IBindingList is an appropriate response;
while it's possible to hook into the ListChanged event, we could as well manipulate the series' DataPointCollection directly.
let formloop = async{
while not chart.IsDisposed do
series.Points.RemoveAt 0
series.Points.AddY(rnd.NextDouble()) |> ignore
do! Async.Sleep 100 }
Finally I'd like to point out this contribution by John Atwood which adresses both points raised by Fyodor; the data binding issue (by not using it) and the UI-thread safety issue.
I have a JavaFX app which contains a line chart. I want users to be able to select the color of each series in the chart. Since the selection is dynamic I can't use static CSS to set the colors. I also have other controls that I need to set to the same color as the associated series. It's possible to set the line color of a series dynamically using code like this:
series.getNode().setStyle("-fx-stroke: " + color + ";");
That works well and I can use the user-specified color on the associated controls.
My problem is that I also need to set the color of the symbols for each series to the same color. I can't find any way to do that dynamically. All of the tutorials, documentation, and posts that I've read on the topic point to the static CSS approach.
Most charting widgets make this sort of thing very easy to do, but I've found no clues here or on the Oracle forums. Any guidance would be greatly appreciated.
-- Update --
I've found no way to do this other than to enumerate every data point in every series, grab the associated symbol node and set the style individually. Not what I was hoping for. In the process I realized that the default Node allocated for a symbol is a StackPane. I didn't need that flexibility so I replaced it with a Rectangle. This made rendering faster.
I'm late to the game, but maybe someone can use my solution. What worked for me, was iterating through every item in the data series and setting the CSS style for each one.
for (int index = 0; index < series.getData().size(); index++) {
XYChart.Data dataPoint = series.getData().get(index);
Node lineSymbol = dataPoint.getNode().lookup(".chart-line-symbol");
lineSymbol.setStyle("-fx-background-color: #00ff00, #000000; -fx-background-insets: 0, 2;\n" +
" -fx-background-radius: 3px;\n" +
" -fx-padding: 3px;");
}
I was stuck with a similar problem. I don't know upfront which data is going to be added to the graph, so I can't make use of a fixed stylesheet.
I came up with this solution. This code listens for new series added to graph. For every added series, it will create a new listener for data added to the series.
This listener will look up which series this is, the 0th, 1st, etc and then find the two nodes for the coloring of the line and of the legend/symbol.
As soon as it has set both, it can unsubscribe.
Problem can be that the legend/symbol node is not available yet when you receive the callback on the first added datapoint.
I'm aware it's very convoluted and I'm open to hear improvements.
At least it will give you the option to dynamically set the color to anything you want.
final LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis());
final ObservableList<Series<Number, Number>> series = chart.getData();
series.addListener(new ListChangeListener<Series<Number, Number>>() {
#Override
public void onChanged(Change<? extends Series<Number, Number>> change) {
ObservableList<? extends Series<Number, Number>> list = change.getList();
for (final Series<Number, Number> serie : list) {
serie.getData().addListener(new ListChangeListener<Data<Number, Number>>() {
#Override
public void onChanged(Change<? extends Data<Number, Number>> ignore) {
int index = series.indexOf(serie);
Set<Node> nodes = chart.lookupAll(".series" + index);
boolean isStyleSet = false;
for (Node n : nodes) {
if (StringUtils.isEmpty(n.getStyle())) {
String css = "-fx-stroke: %s; -fx-background-color: %s, white; ";
String color = //assign dynamically here, for instance based on the name of the series
n.setStyle(String.format(css, color, color));
isStyleSet = true;
}
}
if (!isStyleSet & nodes.size() > 1) {
serie.getData().removeListener(this);
}
}
});
}
}
});
I had a problem which might be slightly different (possibly more complex); I needed to style some nodes of a series one color, others within the same series another color (I also needed to be able to change the allocation of color dynamically). I am working in JavaFx 2.2; I have css-styling, but of course that does not help here. I could not find my issue addressed anywhere; this was the closest I've found.
I just want to say that I could not get "series.getNode().setStyle("-fx-stroke: " + color + ";")" to work. However, using "-fx-background" instead does work. I hope this helps someone.
I found this solution.
If type of chart is pie, how specify parameters (x,y) of highlight(x, y)?
Thanks
Sorry for my bad English.
Unfortunately, flot doesn't expose the pie highlighting code to the user. So we are pretty much out of luck, but what may work for you is synthesizing a click event at the appropriate place on the page:
$("#highligher").click(function () {
var e = jQuery.Event('click');
e.pageX = 250; //add a made up x/y coordinate to the click event
e.pageY = 250;
$('#plot canvas:first').trigger(e); //trigger the click event on the canvas
});
Here it is in action: http://jsfiddle.net/ryleyb/mHJm5/
The problem is you have to know where the slice you want to highlight is already. This would be easy enough to set if the graph is static. If it's a dynamic graph, you'd have to dig into the source of the pie code to figure out how to calculate where the pie slice is. It might be easier in that case to just have a copy of all the pie functions and manually draw on the pie overlay.
Just got this working by altering a few things...
I changed highlight and unhighlight in jquery.flot.pie.js to pieHighlight and pieUnhighlight.
Then, after these two lines in jquery.flot.pie.js...
plot.hooks.processOptions.push(function(plot, options) {
if (options.series.pie.show) {
I added...
plot.highlight = pieHighlight;
plot.unhighlight = pieUnhighlight;
We're maintaining selection state outside of the chart (as a backbone model). When a selection event (click) occurs, we set the selected slice in the model. When selection changes, we refresh the chart using a selection color for the pie slices that are selected.
var data = [];
var color = slice.index == selected ? '#FF0000' : '#0000FF';
data.push({label:slice.Label,data:slice.Value,color:color});
The snippet above uses blue for all non-selected slices, red for the selected slice. Your color logic will be more sophisticated.
NOTE: You can also use rgba CSS for the colors, which gives a really nice effect. For example:
var color = slice.index == selected ? 'rgba(0,0,255,1)' : 'rgba(0,0,255,0.5)';
I have a custom view in which I am trying to draw text using a Windows font (calibri.ttf). However, I am getting different behaviour between using the DrawString & ShowText functions.
I have embedded the font as part of the app (added it to the UIAppFonts list & set it's build action to Content) and I all works fine when I use the DrawString method from my custom UIView e.g.
Code
public override void Draw (System.Drawing.RectangleF rect)
{
base.Draw (rect);
DrawString("Calibri font via DrawString", new RectangleF(10, 10, 100, 100), UIFont.FromName("Calibri", 16f));
}
Result
However, if I attempt to draw the same text this time using ShowText it appears as if the font isn't encoding the text correctly, or the character mapping is wrong.
Code
public override void Draw (System.Drawing.RectangleF rect)
{
base.Draw (rect);
var ctx = UIGraphics.GetCurrentContext();
ctx.SelectFont("Calibri", 16f, MonoTouch.CoreGraphics.CGTextEncoding.FontSpecific);
ctx.ShowTextAtPoint(10, 10, "Calibri font via ShowText using SelectFont");
}
Result
UPDATE - Here is what I get if I use MacRoman encoding instead of FontSpecific:
I have also tried loading in the font manually and using that but then I get nothing at all, it's like it doesn't recognise the font e.g.
var fontPath = NSBundle.MainBundle.PathForResource("calibri", "ttf");
var provider = new CGDataProvider(fontPath);
var font = CGFont.CreateFromProvider(provider);
var ctx = UIGraphics.GetCurrentContext();
ctx.SetFont(font);
ctx.ShowTextAtPoint(10, 20, "Calibri font via ShowText using SetFont");
I know that DrawString is UIKit & ShowText is CG (Quartz) so I understand that there may be differences. However, from what I gathered the only difference with DrawString was it corrected the issue with the difference in Offset (CG being at the bottom left/UIKit being at the top left).
NOTE - The underlying problem I have is I need to use this font to draw text onto a layer via a custom CALayerDelegate. I don't have access to the DrawString function from in there, therefore, the only way I can see to draw the text is via ShowText. Alternative solutions are welcome!
This really looks like an encoding issue. Try using CGTextEncoding.MacRoman instead of CGTextEncoding.FontSpecific (even Arial wouldn't render, as expected, with FontSpecific).
UPDATE Oct 12th
a) your last code sample won't work because Apple doc specifically states not to use SetFont and ShowText together. Quote follow:
Quartz uses font data provided by the system to map each byte of the array through the encoding vector of the current font to obtain the glyph to display. Note that the font must have been set using CGContextSelectFont. Don’t use CGContextShowText in conjunction with CGContextSetFont.
b) the CGTextEncoding.MacRoman code works with several other iPhone-supplied fonts. I'm beginning to suspect it's something about the Calibri.ttf font itself that is not supported by iOS.