How to set column width in adaptive card so its uniform - botframework

I'm trying to ask user informations in adaptive card. But the column size is varying because of which the card looks clumsy. How can I make it look good with uniform gap between each columnset columns. I tried using width as "Auto" and "Stretch" I even tried setting width with "50px" and "100px" but I didn't achieve.
Kindly help me on this.
Edit 1: Adding Code
I'm creating card in C#
Here is my code
AdaptiveCard card = new AdaptiveCard()
{
Body = new List<AdaptiveElement>()
{
new AdaptiveColumnSet()
{
Columns = new List<AdaptiveColumn>
{
new AdaptiveColumn()
{
Items = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text="*First Name",
Weight = AdaptiveTextWeight.Bolder
},
} ,
Width = AdaptiveColumnWidth.Auto
},
new AdaptiveColumn()
{
Width = AdaptiveColumnWidth.Auto,
Separator = true,
Items=new List<AdaptiveElement>()
{
new AdaptiveTextInput()
{
Id = "FirstName",
MaxLength = 300,
Style = AdaptiveTextInputStyle.Text,
},
}
}
}
}, // First Name
new AdaptiveColumnSet()
{
Columns = new List<AdaptiveColumn>
{
new AdaptiveColumn()
{
Items = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text="Middle Name",
Weight = AdaptiveTextWeight.Bolder
},
}
},
new AdaptiveColumn()
{
Width = "stretch",
Separator = true,
Items=new List<AdaptiveElement>()
{
new AdaptiveTextInput()
{
Id = "MiddleName",
MaxLength = 300,
Style = AdaptiveTextInputStyle.Text,
},
}
}
}
},
new AdaptiveColumnSet()
{
Columns = new List<AdaptiveColumn>
{
new AdaptiveColumn()
{
Items = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text="*Last Name",
Weight = AdaptiveTextWeight.Bolder
},
}
},
new AdaptiveColumn()
{
Width = "stretch",
Separator = true,
Items=new List<AdaptiveElement>()
{
new AdaptiveTextInput()
{
Id = "LastName",
MaxLength = 300,
Style = AdaptiveTextInputStyle.Text,
},
}
}
}
},
new AdaptiveColumnSet()
{
Columns = new List<AdaptiveColumn>
{
new AdaptiveColumn()
{
Items = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text="*Date Of Birth",
Weight = AdaptiveTextWeight.Bolder
},
}
},
new AdaptiveColumn()
{
Width = "stretch",
Separator = true,
Items=new List<AdaptiveElement>()
{
new AdaptiveDateInput()
{
Id = "DoB",
},
}
}
}
}, // Date Of Birth
}
};

first of all you could control your card a lot easier if you'd have a look at templating.
Creating cards in code like that is soooort of deprecated, you can still use it but using json directly and templating is way more convinient.
To answer your question, yes you can can set width of columns in either pixel, stretch, automatic or weighted. Similar to this example:
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": 25,
"horizontalAlignment": "Center",
"verticalContentAlignment": "Center"
},
{
"type": "Column",
"width": "stretch"
}
]
You have to set all the label columns in your example to the same size. Easiest is by just setting them to a fixed width or weighted.
If this does not work, make sure the term is used correct, you have to use a lower case "stretch" and width when weighted has to be an integer, You can try this in the designer. Adaptive Cards as such doesn't really care about "Stretch" or "stretch" but webchat for example has some issues around that, also the card version has to match what your host supports.
Easiest way, and thats where templating comes in again, is to design and try your card in the designer. If its fine in the designer it usually is in the host you want to render the card.
Hope thats helpful for you

Related

C# Microsoft Bot Schema Teams Image cropping off the top and bottom

I had a question regarding Teams Microsoft Bot framework. Whenever my bot sends an adaptive card, the top and the bottom of the photo continue to cut off. Inside the adaptive card is the hero card image, it seems I'm unable to resize it to make it fit. I've tried making the image smaller and larger to see if that would fix the issue. Below is a screenshot of the issue I am having.
I'm hoping someone has run into the same issue and if this is fixable or not. Thank you.
Image being used; https://imgur.com/a/hkcSkrJ
public async Task<SendResult> SendAsync(NotificationTeamsAttempt attempt)
{
try
{
if (string.IsNullOrWhiteSpace(attempt.ConversationId))
throw new Exception("Conversation Id is required.");
if (string.IsNullOrWhiteSpace(attempt.ServiceUrl))
throw new Exception("Service Url is required.");
using (var connector = new ConnectorClient(new Uri(attempt.ServiceUrl), _clientId, _clientSecret))
{
var activity = MessageFactory.Text("");
activity.Attachments.Add(attempt.Attachment());
activity.Summary = attempt.Summary();
var response = await connector.Conversations.SendToConversationAsync(attempt.ConversationId, activity);
return new SendResult
{
IsSuccess = true,
DispatchId = response.Id
};
}
}
catch (Exception exception)
{
return new SendResult
{
IsSuccess = false,
Exception = exception
};
}
}
public override Attachment Attachment()
{
var card = new ThumbnailCard
{
Title = "Post submitted for review by " + DraftAuthor,
Subtitle = DraftTitle,
Text = DraftDescription,
Images = new List<CardImage>(),
Buttons = new List<CardAction>()
};
if (!string.IsNullOrWhiteSpace(TeamsUrl))
{
card.Buttons.Add(new CardAction
{
Type = "openUrl",
Title = "Review in Teams",
Value = TeamsUrl.Replace("null", $"%22post%7C{DraftId}%7C{DraftId}%22")
});
}
if (!string.IsNullOrWhiteSpace(SPUrl))
{
card.Buttons.Add(new CardAction
{
Type = "openUrl",
Title = "Review in SharePoint",
Value = $"{SPUrl}?postId={DraftId}&sourceId={DraftId}"
});
}
return card.ToAttachment();
}
Please disregard the black lines I've added. But below you can see where the image is cropping off.
Image of the cropping.
Moving comment to answer -
We are using the below JSON and we get the perfect image without cropping -- { "type": "AdaptiveCard", "body": [ { "type": "TextBlock", "size": "Medium", "weight": "Bolder", "text": "card image test" }, { "type": "Container", "items": [ { "title": "Public test 1", "type": "Image", "url": "https://i.imgur.com/OiJNN03.jpeg" } ] } ], "$schema": "adaptivecards.io/schemas/adaptive-card.json", "version": "1.0" }

Adaptive card column style is working in Adaptive Designer but not in WebChat

I'm using Adaptive Card version 1.2 and I'm trying to add style for Adaptive Column. The style is getting reflected in Adaptive Designer(Web Chat). But the same if I try to check in hosted Web Chat styles are not getting reflected. Also I would like to mention that I'm creating Adaptive card using C#.
But it is looking like this
The i'm using this for webchat (https://cdn.botframework.com/botframework-webchat/latest/webchat.js)
Here is my C# code
AdaptiveCard card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 0));
card.Body.Add(new AdaptiveTextBlock()
{
Text = $"Hello User",
HorizontalAlignment = AdaptiveHorizontalAlignment.Center
});
card.Body.Add(new AdaptiveTextBlock()
{
FontType = AdaptiveFontType.Monospace,
Text = "Would you like to rate us",
Color = AdaptiveTextColor.Good,
Weight = AdaptiveTextWeight.Bolder,
HorizontalAlignment = AdaptiveHorizontalAlignment.Center,
Separator = true
});
card.Body.Add(new AdaptiveColumnSet()
{
Separator = true,
Columns = new List<AdaptiveColumn>()
{
new AdaptiveColumn()
{
Bleed = true,
Width = AdaptiveColumnWidth.Stretch,
Style = AdaptiveContainerStyle.Emphasis,
SelectAction = new AdaptiveSubmitAction()
{
Title = "1",
DataJson = "{ \"Type\": \"1\" }"
},
Items = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text = "1",
}
}
},
new AdaptiveColumn()
{
Bleed = true,
Width = AdaptiveColumnWidth.Stretch,
Style = AdaptiveContainerStyle.Accent,
SelectAction = new AdaptiveSubmitAction()
{
Title = "2",
DataJson = "{ \"Type\": \"2\" }"
},
Items = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text = $"2"
}
}
},
new AdaptiveColumn()
{
Bleed = true,
Width = AdaptiveColumnWidth.Stretch,
Style = AdaptiveContainerStyle.Good,
SelectAction = new AdaptiveSubmitAction()
{
Title = "3",
DataJson = "{ \"Type\": \"3\" }"
},
Items = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text = $"3"
}
}
},
new AdaptiveColumn()
{
Bleed = true,
Width = AdaptiveColumnWidth.Stretch,
Style = AdaptiveContainerStyle.Attention,
SelectAction = new AdaptiveSubmitAction()
{
Title = "4",
DataJson = "{ \"Type\": \"4\" }"
},
Items = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text = $"4"
}
}
}
}
});
Please help me on this

Programatically placing marker in drilldown map amchart

I have a map which is a drilldown map. It goes from continents view to a country view.
My goal is to place markers dynamically based on the selected country (after the drilldown).
Here in this example I want to place a marker in Berlin (Germany) however this marker doesn't get created.
Example: https://codepen.io/ms92o/pen/gjMPEJ?editors=1111
var map = AmCharts.makeChart("chartdiv", {
"type": "map",
"theme": "light",
"areasSettings": {
"autoZoom": true,
"rollOverOutlineColor": "#9a7bca",
"selectedColor": "#9a7bca",
"color": "#a791b4",
"rollOverColor": "#9a7bca"
},
"zoomControl": {
"buttonFillColor": "#a6bd7f",
"buttonRollOverColor": "#9a7bca"
},
"dataProvider": continentsDataProvider,
"objectList": {
"container": "listdiv"
},
"listeners": [{
"event": "clickMapObject",
"method": function (event) {
console.log(event);
// TODO: how to create some markers here based on the selected country?
let rep = { title: 'Berin', latitude: '52.520', longitude: '13.409779' };
rep.svgPath = targetSVG;
rep.zoomLevel = 3;
rep.scale = 1.2;
rep.label = rep.title;
map.dataProvider.images.push(rep);
}
}]
});
You need to call the map's validateNow()/validateData() methods whenever you update the map with new areas/markers or changes to its properties. The caveat of these calls is that they reset the map's zoom position unless you modify the dataProvider's zoom properties (zoomLevel, zoomLatitude and zoomLongitude), which also affects the home button unless you reset them after the fact.
Here's a solution that adds the marker while making sure the zoom level sticks and fixes the home button afterward:
"listeners": [{
"event": "clickMapObject",
"method": function (event) {
let rep = { title: 'Berin', latitude: '52.520', longitude: '13.409779' };
rep.svgPath = targetSVG;
rep.zoomLevel = 3;
rep.scale = 1.2;
rep.label = rep.title;
map.dataProvider.images.push(rep);
//delay the update so that the click+zoom action still occurs before
//adding the marker
setTimeout(function() {
//preserve current zoom level on update
map.dataProvider.zoomLevel = map.zoomLevel();
map.dataProvider.zoomLatitude = map.zoomLatitude();
map.dataProvider.zoomLongitude = map.zoomLongitude();
map.validateNow(); //add marker
//reset the zoom values so that the home button zooms
//completely out when clicked
map.dataProvider.zoomLevel = 0;
map.dataProvider.zoomLatitude = undefined;
map.dataProvider.zoomLongitude = undefined;
}, (map.zoomDuration + .5) * 1000);
}
}]
Updated codepen

Handsontable: Updating a cell renderer at runtime

I'm using handsontable, and I want to change the background color of a cell if its value is edited and changed. I can do this easily if my data source is an array of arrays (see fiddle: http://jsfiddle.net/chiman24/3o2c3c7m/).
document.addEventListener("DOMContentLoaded", function() {
// Row Styles
var blank = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.backgroundColor = '#ABAAAA'
};
var align = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.verticalAlign = 'middle';
td.style.fontWeight = 'bold';
};
var highlight1 = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.backgroundColor = '#BDD7EE';
td.style.textAlign = 'right';
};
var changedBackgroundColor = '#cbd9e4';
var defaultBackgroundColor = 'white';
var hasChanged = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.backgroundColor = changedBackgroundColor;
};
var noChange = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.backgroundColor = defaultBackgroundColor;
};
var data = [
["1", "Hear us from heaven", "New Life Worship",
"Anderson, Jared", "something"
],
["2", "Spirit Break Out", "Kim Walker", "Walker, Kim",
"Still Believe"
]
],
dataCopy = [
["1", "Hear us from heaven", "New Life Worship",
"Anderson, Jared", "something"
],
["2", "Spirit Break Out", "Kim Walker", "Walker, Kim",
"Still Believe"
]
],
container = document.getElementById('example1'),
hot1;
//Table Row and Col Options
hot1 = new Handsontable(container, {
data: data,
fixedColumnsLeft: 1,
columnSorting: true,
colHeaders: ["id", "title", "artist", "author", "album"],
columns: [{
type: "text"
}, {
type: "text"
}, {
type: "text"
}, {
type: "text"
}, {
type: "text"
}]
});
hot1.addHook('afterChange', afterChange);
function afterChange(changes, source) {
if (source == 'edit' || source == 'autofill') {
$.each(changes, function(index, element) {
var change = element;
var rowIndex = change[0];
var columnIndex = change[1];
var oldValue = change[2];
var newValue = change[3];
var cellChange = {
'rowIndex': rowIndex,
'columnIndex': columnIndex
};
if (oldValue != newValue) {
var cellProperties = hot1.getCellMeta(
rowIndex, columnIndex);
if (newValue != dataCopy[rowIndex][
columnIndex
]) {
cellProperties.renderer = hasChanged;
} else { //data changed back to original value.
cellProperties.renderer = noChange;
}
hot1.render();
}
});
}
}
});
// noSideScroll class added to fix some containers while side scrolling the table
$(window).scroll(function() {
$('.noSideScroll').css({
'left': $(this).scrollLeft()
});
});
However, when using an array of objects, I can't get it to work. (see fiddle: http://jsfiddle.net/chiman24/24mpavga/).
var data = [{
"id": 1,
"title": "First Loved Me",
"artist": "Israel and New Breed",
"author": "Houghton, Israel",
"album": "Covered: Alive In Asia"
}, {
"id": 2,
"title": "One Thing Remains",
"artist": "Israel and New Breed",
"author": "Houghton, Israel",
"album": "Covered: Alive In Asia"
}],
dataCopy = [{
"id": 1,
"title": "First Loved Me",
"artist": "Israel and New Breed",
"author": "Houghton, Israel",
"album": "Covered: Alive In Asia"
}, {
"id": 2,
"title": "One Thing Remains",
"artist": "Israel and New Breed",
"author": "Houghton, Israel",
"album": "Covered: Alive In Asia"
}],
container = document.getElementById('example1'),
hot1;
//Table Row and Col Options
hot1 = new Handsontable(container, {
data: data,
fixedColumnsLeft: 1,
columnSorting: true,
colHeaders: ["id", "title", "artist", "author", "album"],
columns: [{
data: "id"
}, {
data: "title"
}, {
data: "artist"
}, {
data: "author"
}, {
data: "album"
}]
});
hot1.addHook('afterChange', afterChange);
function afterChange(changes, source) {
if (source == 'edit' || source == 'autofill') {
$.each(changes, function(index, element) {
var change = element;
var rowIndex = change[0];
var columnIndex = change[1];
var oldValue = change[2];
var newValue = change[3];
var cellChange = {
'rowIndex': rowIndex,
'columnIndex': columnIndex
};
if (oldValue != newValue) {
var cellProperties = hot1.getCellMeta(
rowIndex, columnIndex);
if (newValue != dataCopy[rowIndex][
columnIndex
]) {
cellProperties.renderer = hasChanged;
} else { //data changed back to original value.
cellProperties.renderer = noChange;
}
hot1.render();
}
});
}
}
Is there a way to accomplish this? I want to get it working using an array of objects because my data coming from the server will be in JSON format. I've scoured the handsontable documentation for a couple of days to no avail. Any help will be much appreciated. Thanks.
I got some help from the handsontable github forum.
Apparently, if the data source is an array of objects, then when calling "getCellMeta", instead of passing in a numerical column index, you have to pass in the column index as a property like this:
hot.getCellMeta(2, hot.propToCol(columnIndex));
Here's the updated demo
Other way to change background color of cell is use the Cell Option
...
if (oldValue != newValue){
aCell.push(
{ row: rowIndex,
col: hot.propToCol(columnIndex),
className: "cssWithBackgroundColor" });
hot.updateSettings({ cell: aCell });
}
If the user undo change you can
if ( source == 'UndoRedo.undo'){
aCell.pop();
hot.updateSettings({ cell: aCell });
}

Kendo grid resizable column width

The grid columns may be resizable. I want to store user-adjusted columns width and restore them when the next session starts.
The best way to store columns width I've found is the following:
var element = $('#grid').kendoGrid({
...
resizable: true,
columnResize: function(e) {
var state = {};
this.columns.every(function(c,i) {
state[c.field] = c.width;
return true;
});
var state_txt = JSON.stringify(state);
localStorage['profile_userprofile_grid_column_width'] = state_txt;
}
}
Now I want to restore column width saved in the previous user session. I can read columns width from the storage:
var state = JSON.parse(localStorage['profile_userprofile_grid_column_width']);
Does somebody know some elegant way to apply these values back to the grid if it is already created at this time? The resize handle does it internally, so it is possible, but the code doing it in the grid source is ugly.
You can trigger the columnResize event post initilisation as shown below
function grid_columnResize(e) {
// Put your code in here
console.log(e.column.field, e.newWidth, e.oldWidth);
}
$("#grid").kendoGrid({
columns: [
{ field: "name" },
{ field: "age" }
],
dataSource: [
{ name: "Jane Doe", age: 30 },
{ name: "John Doe", age: 33 }
],
resizable: true
});
var grid = $("#grid").data("kendoGrid");
grid.bind("columnResize", grid_columnResize);
Documentation
This is an old question, but here is what we have. Function handles column widths and groups.
var _updateResultsGridColumns = function(columns, groups) {
var kendoGrid = $resultsGrid.data("kendoGrid");
if (kendoGrid) {
kendoGrid.setOptions({
columns: columns,
});
var dataSource = kendoGrid.dataSource;
dataSource.group(groups);
kendoGrid.setDataSource(dataSource);
}
}

Resources