前三天我们把TreeView给显示出来了,不过光是显示显然是不够的。TreeView的UI还需要根据用户的各种操作和不同的属性设置产生不同的效果变化,比如:Actived、Selected、Checked等。下面就来设计并实现TreeView的UI动态更新问题。
对于控件开发,不管是Web上的还是WinForm上,使用鼠标和键盘来触发的UI更新和使用程序代码来触发更新是同等地位的。所以我们在设计中最好不要在事件处理函数里面去直接的更新控件UI,为什么呢?因为这样的更新是单向服务的(虽然可以在程序中去调用HTML元素的事件回调,不过语法上太蹩脚了),当我们需要在程序中去更新控件UI,比如设置Selected状态,就需要再写一套代码来从程序的角度去更新UI元素的属性。这时我们会发现前面那种单向服务的代码,其实是完全可以被后者包括(在功能上)的。继续说Selected,当我们用鼠标点击TreeNode时,我们可以从事件响应代码中直接拿到event.srcElement(这就是显示TreeNode的HTML元素),我们如果在此时修改这个元素的属性来显示Selected状态,很容易。可是如果我们还需要在程序中去调用TreeNode.SetSelected( true)来得到Selected效果时,前面所说的单向服务的代码就都被后者(SetSelected)包含了。
哦,那么我们就实现n个方法,比如:SetActived(bool)、SetSelected(bool)、SetChecked(bool),在这些方法中去分别修改控件UI元素属性。由于我们在JS控件中,需要在脚本对象和DHMTL对象中来回引用和查找,并且有些状态的UI的显示还存在优先级的问题(比如Checked和Selected都需要文字颜色变化,但Selected优先级高于Checked)。所以我们也不在每个SetXXX函数中去更新UI元素的属性,而是使用一个统一的函数,来对整个控件的UI更新作出处理。于是我们定义了一个ApplyUIChange()函数,在这里面处理所有和UI呈现有关属性的处理,并具体修改UI元素的属性。
SetXXX方法实现为:
它们只负责维护 控件对象的属性状态,所有的控件的UI相关操作交由 this.ApplyUIChange()去处理。这个设计也和WinForm控件中修改属性后的显示调用Invalidate()的设计类似,一切的UI更新都在OnPaint中做。innerCache相关操作在这里不用关心,那是用来记录TreeView实例中被Checked和Selected的Nodes用的,以后再讲。
ApplyUIChange代码实现如下:
在这个方法中,它只用关心属性间的优先级和对UI元素属性的修改,同时这也是程序中唯一修改HTML元素属性的地方。不过本控件中Node的子树的Expand和Collapse的UI元素属性更新没有放在这里,这时因为放在它们独自的方法中也很清晰(不存在和其它UI显示冲突的问题)。也就是说我们虽然有设计原则,不过有时可以使用别的简便方法实现时,也不用太拘泥于原则。设计本身就是妥协,而且我们实际需要的就是简便清晰的实现,而不是要完美的原则。
附各中TreeView的外观样式变化:
对于控件开发,不管是Web上的还是WinForm上,使用鼠标和键盘来触发的UI更新和使用程序代码来触发更新是同等地位的。所以我们在设计中最好不要在事件处理函数里面去直接的更新控件UI,为什么呢?因为这样的更新是单向服务的(虽然可以在程序中去调用HTML元素的事件回调,不过语法上太蹩脚了),当我们需要在程序中去更新控件UI,比如设置Selected状态,就需要再写一套代码来从程序的角度去更新UI元素的属性。这时我们会发现前面那种单向服务的代码,其实是完全可以被后者包括(在功能上)的。继续说Selected,当我们用鼠标点击TreeNode时,我们可以从事件响应代码中直接拿到event.srcElement(这就是显示TreeNode的HTML元素),我们如果在此时修改这个元素的属性来显示Selected状态,很容易。可是如果我们还需要在程序中去调用TreeNode.SetSelected( true)来得到Selected效果时,前面所说的单向服务的代码就都被后者(SetSelected)包含了。
哦,那么我们就实现n个方法,比如:SetActived(bool)、SetSelected(bool)、SetChecked(bool),在这些方法中去分别修改控件UI元素属性。由于我们在JS控件中,需要在脚本对象和DHMTL对象中来回引用和查找,并且有些状态的UI的显示还存在优先级的问题(比如Checked和Selected都需要文字颜色变化,但Selected优先级高于Checked)。所以我们也不在每个SetXXX函数中去更新UI元素的属性,而是使用一个统一的函数,来对整个控件的UI更新作出处理。于是我们定义了一个ApplyUIChange()函数,在这里面处理所有和UI呈现有关属性的处理,并具体修改UI元素的属性。
SetXXX方法实现为:
TreeNode.prototype.SetChecked =
function(isCheck)
{
var innerCache = this.m_Tree.m_InnerCache;
if ( isCheck )
{
if ( !innerCache.m_Checkeds.Contains( this) )
{
innerCache.m_Checkeds.Add( this);
}
}
else
{
innerCache.m_Checkeds.Remove( this);
}
this.m_Checked = isCheck;
this.ApplyUIChange();
};
TreeNode.prototype.SetSelected = function(isSelected)
{
var innerCache = this.m_Tree.m_InnerCache;
if ( isSelected )
{
if ( !innerCache.m_Selecteds.Contains( this) )
{
innerCache.m_Selecteds.Add( this);
}
}
else
{
innerCache.m_Selecteds.Remove( this);
}
this.m_Selected = isSelected;
this.ApplyUIChange();
};
{
var innerCache = this.m_Tree.m_InnerCache;
if ( isCheck )
{
if ( !innerCache.m_Checkeds.Contains( this) )
{
innerCache.m_Checkeds.Add( this);
}
}
else
{
innerCache.m_Checkeds.Remove( this);
}
this.m_Checked = isCheck;
this.ApplyUIChange();
};
TreeNode.prototype.SetSelected = function(isSelected)
{
var innerCache = this.m_Tree.m_InnerCache;
if ( isSelected )
{
if ( !innerCache.m_Selecteds.Contains( this) )
{
innerCache.m_Selecteds.Add( this);
}
}
else
{
innerCache.m_Selecteds.Remove( this);
}
this.m_Selected = isSelected;
this.ApplyUIChange();
};
它们只负责维护 控件对象的属性状态,所有的控件的UI相关操作交由 this.ApplyUIChange()去处理。这个设计也和WinForm控件中修改属性后的显示调用Invalidate()的设计类似,一切的UI更新都在OnPaint中做。innerCache相关操作在这里不用关心,那是用来记录TreeView实例中被Checked和Selected的Nodes用的,以后再讲。
ApplyUIChange代码实现如下:
TreeNodeBase.prototype.ApplyUIChange =
function()
{
if ( this.m_Element.CheckBox )
{
this.m_Element.CheckBox.checked = this.m_Checked;
}
var elmtNode = this.m_Element.Content;
if ( this.m_Selected )
{
elmtNode.style.color = this.Styles('SelectedForeColor');
elmtNode.style.background = this.Styles('SelectedBackColor');
}
else
{
if ( this.m_Checked )
{
elmtNode.style.color = this.Styles('CheckedForeColor');
elmtNode.style.background = this.Styles('CheckedBackColor');
}
else
{
elmtNode.style.color = this.Styles('NormalForeColor');
elmtNode.style.background = this.Styles('NormalBackColor');
}
}
if ( this.m_IsActive )
{
elmtNode.runtimeStyle.textDecoration = 'underline';
}
else
{
elmtNode.runtimeStyle.textDecoration = '';
}
};
{
if ( this.m_Element.CheckBox )
{
this.m_Element.CheckBox.checked = this.m_Checked;
}
var elmtNode = this.m_Element.Content;
if ( this.m_Selected )
{
elmtNode.style.color = this.Styles('SelectedForeColor');
elmtNode.style.background = this.Styles('SelectedBackColor');
}
else
{
if ( this.m_Checked )
{
elmtNode.style.color = this.Styles('CheckedForeColor');
elmtNode.style.background = this.Styles('CheckedBackColor');
}
else
{
elmtNode.style.color = this.Styles('NormalForeColor');
elmtNode.style.background = this.Styles('NormalBackColor');
}
}
if ( this.m_IsActive )
{
elmtNode.runtimeStyle.textDecoration = 'underline';
}
else
{
elmtNode.runtimeStyle.textDecoration = '';
}
};
在这个方法中,它只用关心属性间的优先级和对UI元素属性的修改,同时这也是程序中唯一修改HTML元素属性的地方。不过本控件中Node的子树的Expand和Collapse的UI元素属性更新没有放在这里,这时因为放在它们独自的方法中也很清晰(不存在和其它UI显示冲突的问题)。也就是说我们虽然有设计原则,不过有时可以使用别的简便方法实现时,也不用太拘泥于原则。设计本身就是妥协,而且我们实际需要的就是简便清晰的实现,而不是要完美的原则。
附各中TreeView的外观样式变化:
to be continued . . .
本文转自博客园鸟食轩的博客,原文链接:http://www.cnblogs.com/birdshome/,如需转载请自行联系原博主。